diff options
author | molotkov-and <molotkov-and@ydb.tech> | 2023-08-18 17:20:47 +0300 |
---|---|---|
committer | molotkov-and <molotkov-and@ydb.tech> | 2023-08-18 19:42:07 +0300 |
commit | 73215359bc33e76f5b94d1832a377072bf245cfc (patch) | |
tree | 9cb8ad61d8c3cd107353d42951560ff3cf1b966d /contrib/libs/sasl | |
parent | 1cbfd34a55732f7b1d407986b45e40853f01f2c2 (diff) | |
download | ydb-73215359bc33e76f5b94d1832a377072bf245cfc.tar.gz |
KIKIMR-18220: Enrich token with groups from LDAP
Add ldap functions wrapper and separate in different files for compatibility with different OS.
Add user groups fetching from ldap server.
Limitations:
- Fixed 'memberOf' attribute
- No tests to check how filter for search created
- Fetched groups are returned in event as is.
Diffstat (limited to 'contrib/libs/sasl')
56 files changed, 35453 insertions, 0 deletions
diff --git a/contrib/libs/sasl/AUTHORS b/contrib/libs/sasl/AUTHORS new file mode 100644 index 0000000000..cb70ed8315 --- /dev/null +++ b/contrib/libs/sasl/AUTHORS @@ -0,0 +1,58 @@ +Rob Siemborski <rjs3+@andrew.cmu.edu> wrote and tested the conversion +to the SASLv2 API. + +Ken Murchison <murch@andrew.cmu.edu> worked on the OTP, NTLM, SRP and SQL +plugins, as well as helping to track down bugs as they appear. He also +added support for HTTP authentication. + +Rob Earhart <earhart@cmu.edu> wrote the build/installation procedure, +wrote and tested some of the code, and provided general guidance and +coding advice. + +Leif Johansson <leifj@matematik.su.se> wrote the GSSAPI plugin, with +contributions from Sam Hartman <hartmans@fundsxpress.com>. + +Leandro Santi <lesanti@sinectis.com.ar> added Courier authdaemon support. + +Alexey Melnikov <alexey.melnikov@isode.com> wrote the first pass of the +DIGEST-MD5 and SCRAM plugins and continues to work on them. He also wrote +a good deal of the current Windows support. + +Rainer Schoepf <schoepf@uni-mainz.de> contributed the LOGIN plugin, +based on Tim Martin's PLAIN plugin. + +Simon Loader <simon@surf.org.uk> wrote the MySQL auxprop module. + +Rolf Braun <rbraun@andrew.cmu.edu> wrote the MacOS ports. + +Howard Chu <hyc@highlandsun.com> put a good deal of work into OS/390 +portability, correct building of static libraries, and a slew +of misc. bugfixes. + +Tim Martin <tmartin@andrew.cmu.edu> wrote, debugged, and tested +most of the SASLv1 code. + +Larry Greenfield <leg+sasl@andrew.cmu.edu> complained. a lot. + +Chris Newman <chris.newman@oracle.com> wrote the initial version of the +SASL API, as well as the version 2 SASL API (documented in sasl.h, +saslutil.h, saslplug.h, and prop.h). + +Ryan Troll <ryan@andrew.cmu.edu> started the Windows port, +and both Larry Greenfield and Alexey Melnikov have done more work on it. + +getaddrinfo.c was written by Hajimu UMEMOTO <ume@mahoroba.org> +which is based on the IPv6 code written by KIKUCHI Takahiro +<kick@kyoto.wide.ad.jp> + +Igor Brezac <igor@ipass.net> has done a good deal of work on the saslauthd +LDAP module. + +Jeremy Rumpf <jrumpf@heavyload.net> implemented the credential cache, unified +the different IPC methods under a common framework. + +Fabian Knittel <fknittel@gmx.de> wrote auth_pam plugin, based on +Debian's pwcheck_pam daemon by Michael-John Turner <mj@debian.org>. + +saslauthd was originally contributed by Lyndon Nerenberg on +behalf of MessagingDirect Ltd. diff --git a/contrib/libs/sasl/CMakeLists.darwin-x86_64.txt b/contrib/libs/sasl/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..ef6e652888 --- /dev/null +++ b/contrib/libs/sasl/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,53 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +find_package(OpenSSL REQUIRED) + +add_library(contrib-libs-sasl) +target_compile_options(contrib-libs-sasl PRIVATE + -DCONFIGDIR="/tmp/yamaker/sasl/out/lib/sasl2:/var/empty/cyrus-sasl-2.1.28/etc/sasl2" + -DHAVE_CONFIG_H + -DOBSOLETE_CRAM_ATTR=1 + -DOBSOLETE_DIGEST_ATTR=1 + -DPLUGINDIR="/tmp/yamaker/sasl/out/lib/sasl2" + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(contrib-libs-sasl PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/common + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/include + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/sasldb +) +target_link_libraries(contrib-libs-sasl PUBLIC + OpenSSL::OpenSSL +) +target_sources(contrib-libs-sasl PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/common/plugin_common.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/auxprop.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/canonusr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/checkpw.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/client.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/common.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/config.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/dlopen.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/external.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/md5.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/saslutil.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/server.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/seterror.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/anonymous.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/cram.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/digestmd5.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/otp.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/plain.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/sasldb.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/scram.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/sasldb/db_none.c +) diff --git a/contrib/libs/sasl/CMakeLists.linux-aarch64.txt b/contrib/libs/sasl/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..f73f25503a --- /dev/null +++ b/contrib/libs/sasl/CMakeLists.linux-aarch64.txt @@ -0,0 +1,54 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +find_package(OpenSSL REQUIRED) + +add_library(contrib-libs-sasl) +target_compile_options(contrib-libs-sasl PRIVATE + -DCONFIGDIR="/tmp/yamaker/sasl/out/lib/sasl2:/var/empty/cyrus-sasl-2.1.28/etc/sasl2" + -DHAVE_CONFIG_H + -DOBSOLETE_CRAM_ATTR=1 + -DOBSOLETE_DIGEST_ATTR=1 + -DPLUGINDIR="/tmp/yamaker/sasl/out/lib/sasl2" + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(contrib-libs-sasl PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/common + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/include + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/sasldb +) +target_link_libraries(contrib-libs-sasl PUBLIC + contrib-libs-linux-headers + OpenSSL::OpenSSL +) +target_sources(contrib-libs-sasl PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/common/plugin_common.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/auxprop.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/canonusr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/checkpw.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/client.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/common.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/config.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/dlopen.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/external.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/md5.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/saslutil.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/server.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/seterror.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/anonymous.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/cram.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/digestmd5.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/otp.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/plain.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/sasldb.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/scram.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/sasldb/db_none.c +) diff --git a/contrib/libs/sasl/CMakeLists.linux-x86_64.txt b/contrib/libs/sasl/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..f73f25503a --- /dev/null +++ b/contrib/libs/sasl/CMakeLists.linux-x86_64.txt @@ -0,0 +1,54 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +find_package(OpenSSL REQUIRED) + +add_library(contrib-libs-sasl) +target_compile_options(contrib-libs-sasl PRIVATE + -DCONFIGDIR="/tmp/yamaker/sasl/out/lib/sasl2:/var/empty/cyrus-sasl-2.1.28/etc/sasl2" + -DHAVE_CONFIG_H + -DOBSOLETE_CRAM_ATTR=1 + -DOBSOLETE_DIGEST_ATTR=1 + -DPLUGINDIR="/tmp/yamaker/sasl/out/lib/sasl2" + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(contrib-libs-sasl PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/common + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/include + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/sasldb +) +target_link_libraries(contrib-libs-sasl PUBLIC + contrib-libs-linux-headers + OpenSSL::OpenSSL +) +target_sources(contrib-libs-sasl PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/common/plugin_common.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/auxprop.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/canonusr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/checkpw.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/client.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/common.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/config.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/dlopen.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/external.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/md5.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/saslutil.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/server.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/lib/seterror.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/anonymous.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/cram.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/digestmd5.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/otp.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/plain.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/sasldb.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/plugins/scram.c + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/sasldb/db_none.c +) diff --git a/contrib/libs/sasl/CMakeLists.txt b/contrib/libs/sasl/CMakeLists.txt new file mode 100644 index 0000000000..606ff46b4b --- /dev/null +++ b/contrib/libs/sasl/CMakeLists.txt @@ -0,0 +1,15 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/contrib/libs/sasl/CONTRIBUTING.md b/contrib/libs/sasl/CONTRIBUTING.md new file mode 100644 index 0000000000..6180cc7d9f --- /dev/null +++ b/contrib/libs/sasl/CONTRIBUTING.md @@ -0,0 +1,6 @@ +The Cyrus-SASL project isn't using the Github workflows at the moment. + +It's okay (and helpful!) to open Issues and Pull Requests here, but please +also contact the +[Cyrus-SASL mailing list](https://cyrus.topicbox.com/groups/sasl) +or they may not be noticed by anyone who can action them. diff --git a/contrib/libs/sasl/COPYING b/contrib/libs/sasl/COPYING new file mode 100644 index 0000000000..3f56f3059c --- /dev/null +++ b/contrib/libs/sasl/COPYING @@ -0,0 +1,44 @@ +/* CMU libsasl + * Tim Martin + * Rob Earhart + * Rob Siemborski + */ +/* + * Copyright (c) 1998-2003 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/contrib/libs/sasl/ChangeLog b/contrib/libs/sasl/ChangeLog new file mode 100644 index 0000000000..6a547fa8ea --- /dev/null +++ b/contrib/libs/sasl/ChangeLog @@ -0,0 +1,3490 @@ +2016-10-18 Ken Murchison <murch@andrew.cmu.edu> + * Fixed potential DoS attack on saslauthd/doors (from Oracle) + +2016-06-30 Ken Murchison <murch@andrew.cmu.edu> + * plugins/ntlm.c, otp.c: support OpenSSL 1.1 + +2016-06-14 Ken Murchison <murch@andrew.cmu.edu> + * plugins/digestmd5.c: Fix memory leak in client step 2 + +2016-03-24 Ken Murchison <murch@andrew.cmu.edu> + * auth_rimap.c: Don't hang when IMAP server closes connection + +2016-01-29 Ken Murchison <murch@andrew.cmu.edu> + * Build fixes from Ignacio Casal Quinteiro + +2015-12-26 Ken Murchison <murch@andrew.cmu.edu> + * Build fixes from Ignacio Casal Quinteiro + +2015-11-16 Ken Murchison <murch@andrew.cmu.edu> + * Build fixes from Ignacio Casal Quinteiro + +2015-10-14 Ken Murchison <murch@andrew.cmu.edu> + * Build fixes from Ignacio Casal Quinteiro + +2015-07-17 Ken Murchison <murch@andrew.cmu.edu> + * auth_krb5.c: added krb5_conv_krb4_instance option + +2014-11-17 Ken Murchison <murch@andrew.cmu.edu> + * plugins/digestmd5.c: Fix memory leaks + +2014-11-17 Ken Murchison <murch@andrew.cmu.edu> + * plugins/digestmd5.c: prevent going from step 3 to step 2 + +2013-09-13 Alexey Melnikov <alexey.melnikov@isode.com> + * Fix memory leaks in DIGEST + +2013-08-30 Ken Murchison <murch@andrew.cmu.edu> + * plugins/digestmd5.c: only locate reauth cache when reauth is + enabled + +2013-07-11 Alexey Melnikov <alexey.melnikov@isode.com> + * Treat SCRAM and DIGEST as more secure than PLAIN when selecting + client-side mechanism + +2013-07-11 Alexey Melnikov <alexey.melnikov@isode.com> + * Handle NULL return from crypt() + +2012-11-20 Alexey Melnikov <alexey.melnikov@isode.com> + * Added support for lmdb + +2012-11-19 Alexey Melnikov <alexey.melnikov@isode.com> + * Final 2.1.26 tagged and released by Ken. + +2012-07-06 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_krb5.c: Fixed a crash in the auth_krb5.c + (bug # 2706). Patch by Nalin Dahyabhai. + +2012-07-03 Alexey Melnikov <alexey.melnikov@isode.com> + * config/ltconfig: Fixed incorrect Darwin version matching in ltconfig + (bug # 3713). Patch by Joshua Root. + +2012-06-08 Alexey Melnikov <alexey.melnikov@isode.com> + * Fixed PLAIN/LOGIN authentication failure when using saslauthd + with no auxprop plugins (bug # 3590). + +2012-06-08 Alexey Melnikov <alexey.melnikov@isode.com> + * Added generation of pkg-config .pc file for Cyrus SASL. + Patch by Dilyan Palauzov. + +2012-06-03 Alexey Melnikov <alexey.melnikov@isode.com> + * Correctly updated libtool version for libsasl and its plugins due + to ABI changes (bug # 3692). + +2012-06-02 Alexey Melnikov <alexey.melnikov@isode.com> + * Better error reporting from auth_getpwent.c/auth_shadow.c + (bug # 3134). Based on a patch by Greg A. Woods. + +2012-06-02 Alexey Melnikov <alexey.melnikov@isode.com> + * Improved error logging on failure to load plugins. + Patch by Greg A. Woods. + +2012-05-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/otp.c, plugins/srp.c: Removed calling of EVP_cleanup() + on SRP/OTP plugin shutdown + +2012-05-30 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_httpform.c: Encode the parameter values passed to + auth_httpform, not the whole POST data. + +2012-05-30 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/config.c, saslauthd/cfile.c: Fixed file descriptor leaks + throughout the code (bug # 3702). Slightly reformatted patch + by Manfred Weichel. + +2012-05-29 Alexey Melnikov <alexey.melnikov@isode.com> + * bug in "saslauthd -a rimap" - not reading the whole IMAP greeting + (bug # 3211). Patch from Lutz Mark (via Red Hat) + +2012-05-29 Alexey Melnikov <alexey.melnikov@isode.com> + * Modernize SASL malloc/realloc callback prototypes + +2012-05-29 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslutil.c: Fixed broken logic in get_fqhostname() when + abort_if_no_fqdn is 0 (bug # 3589). Patch by baggins@pld-linux.org + +2012-05-28 Alexey Melnikov <alexey.melnikov@isode.com> + * sasldb/db_berkeley.c, utils/dbconverter-2.c: Added support for + BerkleyDB 5.X or later (Patch by Howard Chu) + +2012-04-20 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/client.c, lib/server.c, lib/saslint.h: Make server and client + side global callbacks private to server.c/client.c respectively + +2012-02-10 Ken Murchison <murch@andrew.cmu.edu> + * plugins/digestmd5.c: better handling of HTTP reauth cases. + +2012-01-28 Ken Murchison <murch@andrew.cmu.edu> + * plugins/digestmd5.c: Correctly send "stale" directive to prevent + clients from (re)promtping for password + +2011-11-25 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gs2.c: Updated GS2 plugin not to lose minor GSS-API + status codes on errors (based on a patch from Ralf Haferkamp + <rhafer@suse.de>) + +2011-11-21 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gssapi.c: Only check out_flags once authentication is + successfully completed + +2011-11-09 Ken Murchison <murch@andrew.cmu.edu> + * cmulocal/sasl2.m4, plugins/gssapi.c, utils/testsuite.c: + Added GSS-SPNEGO plugin which can also be used for HTTP + Negotiate authentication (RFC 4559) + +2011-11-08 Ken Murchison <murch@andrew.cmu.edu> + * plugins/ntlm.c: Flag client-side of NTLM plugin as HTTP-ready + +2011-11-08 Ken Murchison <murch@andrew.cmu.edu> + * include/saslutil.h, lib/config.c, lib/server.c + Added sasl_config_done() to plug a memory leak when using an + application specific config file + +2011-10-07 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gssapi.c: Fixed a segfault in gssapi.c + (patch by Phil Pennock) + +2011-09-22 Alexey Melnikov <alexey.melnikov@isode.com> + * config/ltconfig, saslauthd/config/ltconfig: Fixed Cyrus SASL + build on some versions of Mac OS. + +2011-09-22 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_rimap.c: qstring incorrectly appending + the closing double quote. (Merge from RedHat) + +2011-09-22 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/common.c: unlock the mutex in sasl_dispose if the context + was freed by another thread. (Merge from RedHat) + +2011-09-22 Alexey Melnikov <alexey.melnikov@isode.com> + * Makefile.am: "lib" should be built before "plugins" + (Patch from marcandre.lureau@redhat.com) + +2011-09-22 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslutil.c: MINGW32 doesn't have rand_s + (Patch from marcandre.lureau@redhat.com) + +2011-09-22 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in: Various build fixes for MINGW32 + (including defining sleep()) + (Patch from marcandre.lureau@redhat.com) + +2011-09-15 Alexey Melnikov <alexey.melnikov@isode.com> + * sample/client.c: Added additional typecasts to kill warnings + about incompatible callback types + +2011-09-13 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in, config/ltconfig, config/ltmain.sh: + MacOS X related build fixes: use .plugin when building + SASL plugins, fixed version number calculation, + don't generate multiple symlinks. + Also use LD_RUN_PATH as rpath. (patches by Chris Ridd) + +2011-09-12 Alexey Melnikov <alexey.melnikov@isode.com> + * win32/common.mak: Add _CRT_SECURE_NO_DEPRECATE define + to suppress warnings about use of strdup, snprintf, etc. + +2011-09-12 Alexey Melnikov <alexey.melnikov@isode.com> + * sasldb/db_berkeley.c: + Fixed warnings about incompatible callback types. + +2011-09-12 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/NTMakefile plugins/NTMakefile: + Make sure that copied .c files are only rebuilt when changed. + +2011-09-07 Ken Murchison <murch@andrew.cmu.edu> + * plugins/scram.c: + Fixed 3 memory leaks in SCRAM. Final 2.1.25. + +2011-09-07 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in, plugins/NTMakefile, plugins/cram.c: + Allow use of cmusaslsecretCRAM-MD5 property to be disabled. + +2011-09-02 Alexey Melnikov <alexey.melnikov@isode.com> + * config/config.guess, config/config.sub, + saslauthd/config/config.guess, saslauthd/config/config.sub: + Updated config to the latest GNU snapshot. + +2011-09-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/server.c: Make sure that a failed authorization doesn't preclude + further SASL authentication attempts from working. + +2011-09-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/server.c: Fixed some aspects of mech_avail callback handling + in the server side SASL code. + +2011-09-01 Alexey Melnikov <alexey.melnikov@isode.com> + * config/ltconfig, saslauthd/config/ltconfig: Fix SASL's libtool + MacOS/X 64-bit file magic. (Patch by Kurt Zeilenga) + +2011-09-01 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/scram.c: Fixed some additional Windows warnings and + a memory leak in SCRAM. + +2011-09-01 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/scram.c: Fix size_t * v. unsigned * bug. + (Patch by Kurt Zeilenga) + +2011-09-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/server.c: Fixed a crash caused by aborted SASL authentication + and initiation of another one using the same SASL context. + +2011-09-01 Alexey Melnikov <alexey.melnikov@isode.com> + * include/md5.h, include/sasl.h, include/saslplug.h, lib/auxprop.c, + lib/canonusr.c, lib/client.c, lib/common.c, lib/saslint.h, lib/server.c, + lib/seterror.c, plugins/otp.c, plugins/plugin_common.c, + sasldb/db_berkeley.c, sample/sample-client.c, sample/sample-server.c, + utils/pluginviewer.c, utils/sasldblistusers.c, utils/saslpasswd.c, + utils/testsuite.c: Many of the SASL includes define function pointers + without specifying arguments. In C, the () is treated as unspecified, + rather than (void), hence this is technically not a prototype, + and gcc warns about it. (Patch by Dave Cridland and Alexey Melnikov) + +2011-09-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/server.c: Better server plugin API mismatch reporting + +2011-05-23 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gs2.c, plugins/gs2_token.c, plugins/gs2_token.h, + cmulocal/sasl2.m4: Use draft-josefsson-gss-capsulate-01 if present. + Negative SASL errors are fatal. (Patch from Luke Howard.) + +2011-05-13 Ken Murchison <murch@andrew.cmu.edu> + * include/sasl.h, plugins/digest-md5.c: + Allow for non-persistent connections when using DIGEST-MD5 plugin + for server-side HTTP Digest (RFC 2617). Also make sure that an + HTTP request is handed to plugin when required. + +2011-04-19 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gssapi.c: Fix to build GSSAPI with Heimdal (patch from + Russ Allbery from Debian) + +2011-04-18 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gs2_token.h: Added gs2_token.h for the "make dist" target + (patch by Dan White) + +2011-04-13 Alexey Melnikov <alexey.melnikov@isode.com> + * cmulocal/sasl2.m4: Only enable GS2 plugin if + gss_inquire_mech_for_saslname is defined in gssapi.h + +2011-04-12 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/Makefile.am, plugins/makeinit.sh, plugins/ldapdb.c: + LDAPDB build fixes from Dan White + +2011-04-05 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in, plugins/Makefile.am, plugins/NTMakefile, + plugins/makeinit.sh, lib/staticopen.h, win32/include/config.h: + Enabled SCRAM plugin build + +2011-03-25 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/Makefile.am, plugins/makeinit.sh, plugins/gs2_token.h, + plugins/gs2_token.c, README.GS2, cmulocal/sasl2.m4: GS2 plugin + from Luke Howard + +2011-01-25 Ken Murchison <murch@andrew.cmu.edu> + * include/sasl.h, include/saslplug.h, lib/client.c, lib/common.c, + plugins/digest-md5.c sample/http_digest_client.c: + Allow DIGEST-MD5 plugin to be used for client-side + HTTP Digest (RFC 2617) + +2011-01-21 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/scram.c: Added support for channel bindings to SCRAM-SHA-1. + +2011-01-21 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/client.c, lib/server.c, lib/common.c, lib/saslint.h: Fixed libsasl + to accept *-PLUS SASL mechanism names in client_mech_list/mech_list + options. As *-PLUS mechanism names were synthesized and didn't + correspond to real plugin names, setting client_mech_list to + "SCRAM-SHA-1-PLUS" (for example) was resulting in authentication + failure due to inability to find a matching SASL plugin. + +2011-01-21 Alexey Melnikov <alexey.melnikov@isode.com> + * include/saslplug.h, lib/client.c: Fixed handling of channel bindings + on the client side. The client side was failing to select a suitable + SASL mechanism when the application specified channel bindings, but + didn't make them mandatory to use. In such a configuration, if a + non channel binding capable mechanism was selected through + "client_mech_list" SASL option, sasl_client_start would fail. + For example if the server supports both SCRAM-SHA-1[-PLUS] and + PLAIN and "client_mech_list" was set to "PLAIN", authentication + would never work. + +2011-01-21 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/client.c, lib/server.c: Better default ordering of SASL mechanisms. + Ordering by plugins max_ssf produces wrong result in case an application + using SASL doesn't care about SASL security layers. Before this change + DIGEST-MD5 was always preferred over SCRAM-SHA-1[-PLUS]. In particular + this change takes support for channel bindings into considerations. + +2011-01-19 Ken Murchison <murch@andrew.cmu.edu> + * include/sasl.h, include/saslplug.h, + lib/common.c, lib/server.c, plugins/digest-md5.c: + Changed server-side of HTTP Digest so that the application + must pass an HTTP Request structure (Method/URI/Entity-Body) + rather than just the HTTP Method + +2011-01-19 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/server.c: Server side SASL context should list *-PLUS SASL + mechanisms before the corresponding non-PLUS mechanisms for naive + SASL clients. + +2011-01-19 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/common.c: Fixed some Windows warnings in SASL security layer + handling. + +2011-01-19 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/scram.c: Made the default number of SCRAM hash iterations + configurable using a new SASL option called "scram_iteration_counter". + Also fixed a couple of error messages. + +2011-01-19 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/pluginviewer.c: Fixed some Linux warnings in pluginviewer. + +2011-01-19 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/scram.c: Added support for storing SCRAM secrets in + authPassword attribute. Also added the "scram_secret_generate" option + for controlling if authPassword SCRAM secret should be generated + or not. By default (when not specified) the authPassword SCRAM secret + is NOT generated. + +2011-01-19 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/scram.c: Updated the SCRAM plugin not to use the hardcoded + SCRAM-SHA-1 plugin name in logging. + +2011-01-18 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Use the same username for reauthentication + cache lookup and update. Thanks to Ken for pointing out the + problem. + +2011-01-14 Ken Murchison <murch@andrew.cmu.edu> + * plugins/ntlm.c: Flag NTLM plugin as HTTP-ready + +2011-01-14 Ken Murchison <murch@andrew.cmu.edu> + * include/sasl.h, include/saslplug.h, + lib/common.c, lib/server.c, plugins/digest-md5.c: + Allow DIGEST-MD5 plugin to be used for server-side + HTTP Digest (RFC 2617) + +2010-12-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/server.c: Some reformatting and safer handling of 'free + after SASL server shutdown' condition in server_dispose. + +2010-12-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/server.c: server_idle needs to obey server's SASL mechanism + list from the server context. + +2010-12-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/client.c, lib/saslint.h: Added support for ordering + SASL mechanisms by strength (on the client side), + or using the client_mech_list option. + +2010-12-01 Alexey Melnikov <alexey.melnikov@isode.com> + * include/sasl.h, include/saslplug.h, lib/client.c, lib/common.c, + lib/saslint.h, lib/server.c, sample/Makefile.am, sample/client.c, + sample/server.c: Added support for channel bindings + (patch by Luke Howard). + +2010-12-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslutil.c: Fixed the random number generator on Windows + to actually produce random output on each run. + +2010-12-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/common.c: Updated textual representations of some error + messages + +2010-11-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Eliminated some "signed/unsigned mismatch" + warnings. + +2010-11-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c, plugins/srp.c, plugins/otp.c, + plugins/ntlm.c, plugins/login.c, plugins/cram.c: + Be protective against calling sasl_server_step + once authentication has failed. + +2010-11-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Minimize the number of auxprop lookups + in the server side DIGEST-MD5 plugin for the most common + case when authentication and authorization identities are + the same. + +2010-11-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Updated digestmd5_server_mech_step2() + to be more defensive against empty client input. + +2010-11-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Fixed some memory leaks on failed + plugin initialization. Prevent potential race condition + when freeding plugin state. Set the freed reauthentication + cache mutex to NULL, to make errors due to mutex access + after free more obvious. + +2010-11-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Test against broken UTF-8 based hashes + if calculation using special ISO-8859-1 code fails. + This affected some XMPP clients. Patch by Dave Cridland + <dave.cridland@isode.com>. + +2010-11-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Fixed an interop problem with some + LDAP clients ignoring server advertised realm + and providing their own. + +2009-08-14 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_shadow.c: Rolled back the previous commit + (#define _XOPEN_SOURCE before including unistd.h), + as this seems to break Solaris 8 build. Note that crypt.h + should be present on a Solaris 8 machine, as well is on Debian, + so this shouldn't be a problem. + +2009-08-04 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gssapi.c: Properly set serveroutlen to 0 in one place. + Don't send empty challenge once server context establishment is done, + as this is in violation of the RFC 2222 and its successor. + +2009-07-24 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gssapi.c: Don't send maxbuf, if no security layer + can be established. Added additional checks for buffer lengths. + +2009-05-20 Ken Murchison <murch@andrew.cmu.edu> + * configure.in, cmulocal/sasl2.m4, + config/kerberos_v4.m4, config/plain.m4, config/sasldb.m4, + lib/Makefile.am: Fixes to allow static libs to be built in the + CMU build environment + +2009-05-07 Ken Murchison <murch@andrew.cmu.edu> + * configure.in, include/sasl.h, lib/Makefile.am, + plugins/Makefile.am, saslauthd/configure.in, sasldb/Makefile.am, + win32/common.mak, win32/include/config.h: 2.1.24 + +2009-05-03 Alexey Melnikov <alexey.melnikov@isode.com> + * sample/sample-client.c, sample/sample-server.c, utils/smtptest.c: + Fixed bug # 2895 (passing LF to sasl_decode64) + +2009-05-03 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/NTMakefile: Disabled annoying warnings about use of + deprecated standard C library functions, enabled + warnings about Windows64 portability + +2009-05-03 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in: Added support for SQLite3 + (patch by Maxim Gorbachyov) + +2009-04-27 Ken Murchison <murch@andrew.cmu.edu> + * lib/saslutil.c: Fixed CERT VU#238019 (make sure sasl_encode64() + always NUL terminates output or returns SASL_BUFOVER). + +2009-04-11 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/sql.c: Fixed SQLite lookup function. + Also fixed SASL PLAIN authentication when used with + SQLite auxprop backend. + +2009-04-11 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/dlopen.c: Updated to use .plugin extension on MacOS + +2009-04-08 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/client.c, lib/server.c: Removed unused mutexes + (bug # 3141) + +2009-03-10 Alexey Melnikov <alexey.melnikov@isode.com> + * include/sasl.h, include/saslplug.h, lib/canonusr.c, + lib/checkpw.c, plugins/sasldb.c, plugins/sql.c: + Added direct support for hashed password to auxprop API + +2009-03-10 Alexey Melnikov <alexey.melnikov@isode.com> + * include/sasl.h, lib/canonusr.c, lib/external.c, + plugins/gssapi.c, plugins/kerberos4.c: Make auxprop lookup + calls in SASL GSSAPI/EXTERNAL optional + +2009-03-10 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/sasldb.c: A better fix for spurious 'user not found' + errors caused by an attempt to delete a non-existent property + +2009-02-21 Alexey Melnikov <alexey.melnikov@isode.com> + * include/saslutil.h, lib/saslint.h: Made sasl_config_init public + +2009-02-20 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslint.h, lib/client.c, lib/common.c, lib/server.c: + Make sure that sasl_set_alloc() has no effect once sasl_client_init() + or sasl_server_init() is called [patch from Debian by + fabbe@debian.org] + +2009-02-20 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: GCC 4.4 requires that the #elif + preprocessor directive have a test condition [patch from Debian by + fabbe@paniq.net] + +2009-02-20 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/lak.c: Define LDAP_DEPRECATED so that ldap_get_values + is properly defined when compiling [patch from Debian by + Dann Frazier <dannf@debian.org>] + +2009-02-20 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_sasldb.c: pid_file_lock is created with a mask + of 644 instead of 0644 [patch from Debian by Sam Hocevar <sam@zoy.org>] + +2009-02-20 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_sasldb.c: Include config.h so that MAXHOSTNAMELEN + is available when building on hurd-i386 [patch from Debian + by mbanck@debian.org] + +2009-02-20 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_shadow.c: Define _XOPEN_SOURCE before including + unistd.h, so that crypt is correctly defined [patch from Debian + by dannf@debian.org] + +2009-02-14 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/pluginviewer.c: Code cleanup, improved human readable messages + +2009-02-14 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/config.c: Strip trailing spaces from config file option + values (bug # 3139, bug # 3041) + +2009-02-14 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/otp.c: Don't use a stack variable for an OTP prompt + (bug # 2822) + +2009-02-13 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_getpwent.c: Fixed Solaris build (patch by Leena + Heino for bug # 2666) + +2009-02-13 Alexey Melnikov <alexey.melnikov@isode.com> + * include/saslplug.h, lib/server.c, plugins/anonymous.c, + plugins/gssapi.c, plugins/otp.c: Partial support for the + SASL_FEAT_DONTUSE_USERPASSWD feature + +2009-01-28 Alexey Melnikov <alexey.melnikov@isode.com> + * include/sasl.h, lib/auxprop.c, lib/common.c, lib/server.c: + Don't treat a constraint violation as an error to store an auxprop + property + +2009-01-28 Alexey Melnikov <alexey.melnikov@isode.com> + * include/sasl.h, lib/server.c: Extended libsasl (auxprop) to support + user deletion + +2009-01-28 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/otp.c: Downgrade the failure to store OTP secret to debug level + +2009-01-25 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/windlopen.c: Free handles of shared libraries on Windows + that were loaded but are not SASL plugins (patch by Petr Prazak) + [Bug # 2089]. + +2008-11-23 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/NTMakefile, win32/common.mak: Added support for building + SQLite3 on Windows. + +2008-11-23 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/ldapdb.c: Updated LDAPDB lookup function to match auxprop + API changes + +2008-11-15 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/sql.c: Added SQLITE3 support (patch by Maxim Gorbachyov) + +2008-10-31 Ken Murchison <murch@andrew.cmu.edu> + * lib/saslint.h, lib/server.c: order advertised mechanisms + per the specified 'mech_list' option or by relative "strength" + +2008-10-30 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Fixed more portability warnings. + Fixed some rare memory leaks. More detailed error reporting. + +2008-10-30 Alexey Melnikov <alexey.melnikov@isode.com> + * win32/include/config.h, lib/canonusr.c, lib/config.c, + sasldb/allockey.c, utils/saslpasswd.c, utils/testsuite.c, + sample/sample-server.c, plugins/anonymous.c, plugins/digestmd5.c, + plugins/login.c, plugins/ntlm.c, plugins/otp.c: + Fixed Windows 64 portability and other types of warnings + +2008-10-29 Alexey Melnikov <alexey.melnikov@isode.com> + * win32/common.mak: Added support for building libraries. + Added support for Windows64. + +2008-10-29 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/common.c: Prevent freeing of common state on a subsequent + call to _sasl_common_init. Make sure that the last global callback + always wins. + +2008-10-29 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslint.h, lib/canonusr.c, lib/checkpw.c, lib/client.c, + lib/server.c: Further fixes to auxprop lookup and _sasl_canon_user + cleanup + +2008-10-29 Alexey Melnikov <alexey.melnikov@isode.com> + * include/saslplug.h, lib/auxprop.c, lib/canonusr.c, lib/saslint.h, + plugins/sasldb.c, plugins/sql.c: + Extended SASL auxprop_lookup to return error code + +2008-10-29 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslutil.c: Fixed Mac OS X 10.3 build. + +2008-10-29 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/sql.c: Uninitialized variables cause crash when + the searched user is not found (patch from + Maxim Gorbachyov <maxim.gorbachyov@gmail.com>) + +2008-10-23 Alexey Melnikov <alexey.melnikov@isode.com> + * sasldb/db_berkeley.c: Return SASL_NOUSER instead of SASL_FAIL + when the database file doesn't exist + +2008-10-23 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/checkpw.c: Updated sasl_user_exists so that it can handle + passwordless accounts (e.g. disabled) + +2008-10-23 Alexey Melnikov <alexey.melnikov@isode.com> + * include/saslutil.h, lib/saslint.h, lib/client.c, lib/common.c, + lib/saslutil.c, lib/server.c: Added hostname canonicalization + +2008-10-22 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/NTMakefile, utils/NTMakefile, sample/NTMakefile, + plugins/NTMakefile: Updated to build with VC 8.0 (VC++ 2005) + +2008-10-22 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/NTMakefile: Don't install .exp and .manifest files. + Updated build dependencies. + +2008-10-21 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslint.h, lib/client.c, lib/common.c, lib/server.c: + Implemented sasl_client_done/sasl_server_done + +2008-10-19 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/login.c, plugins/plain.c: Advertise + SASL_SEC_PASS_CREDENTIALS feature in PLAIN and LOGIN + +2008-10-02 Ken Murchison <murch@andrew.cmu.edu> + * lib/checkpw.c: Fixed potential buffer overflow in + saslautd_verify_password(). + +2008-09-30 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/common.c: Fixed sasl_set_mutex() to disallow changing + mutex management functions once sasl_server_init/ + sasl_client_init is called. Failure to do this is causing + a crash while locking mutexes. [Bug # 3083] + +2008-01-24 Ken Murchison <murch@andrew.cmu.edu> + * plugins/ntlm.c: Fixed crash in calculating NTv2 reponse + (patch from Tim Costen from Isode) + +2008-01-23 Ken Murchison <murch@andrew.cmu.edu> + * plugins/ntlm.c, doc/options.html: allow a comma separated + list of servernames in 'ntlm_server' option + (patch from Enrico Persiani <enrico@ninfea-soft.org>) + +2008-01-23 Ken Murchison <murch@andrew.cmu.edu> + * plugins/ldapdb.c, plugins/makeinit.sh, doc/options.html: + Added code to extend ldapdb into a canon_user plugin + in addition to its existing auxprop plugin functionality + (patch from Howard Chu <hyc@symas.com> + and Torsten Schlabach <tschlabach@gmx.net>) + +2008-01-23 Ken Murchison <murch@andrew.cmu.edu> + * saslauthd/auth_rimap.c: fixed bug counting double-quotes in + username/password. Also fixed bug zeroing password. + (patch from Robert Sanderson <rwsiv1@gmail.com>) + +2008-01-23 Ken Murchison <murch@andrew.cmu.edu> + * saslauthd/auth_krb.c: improved diagnostic in the + k5support_verify_tgt() function. Now, detailed krb5 error + information will be given out in the LOG_DEBUG syslog + channel (based on patch from Enrico Scholz + <enrico.scholz@informatik.tu-chemnitz.de>) + +2007-06-13 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/dlopen.c: 64bit HP-UX uses .so for shared libraries + (patch by Nathan Kinder <nkinder@redhat.com>). + +2007-06-13 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Fixed a memory leak in the DIGEST-MD5 + security layer (based on patch from Nathan Kinder + <nkinder@redhat.com>). + +2007-05-14 Alexey Melnikov <alexey.melnikov@isode.com> + * man/*: updated to reference RFC 4422 instead of + RFC 2222. + +2007-03-02 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/sasldb.c, plugins/sql.c: Ignore properties + starting with '*' in the auxprop store function. + +2007-02-14 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Fixed parsing of challenges/ + responses with extra commas. + +2007-01-29 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/gssapi.c: Check that params->serverFQDN is + not NULL before using strlen on it (reported by + Steven Simon <simon.s@apple.com>) + +2006-12-01 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/common.c: Typecast iov_base to (char *), + in case it is defined as "void *" on a platform + like HPUX (Olaf Flebbe). + +2006-11-27 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Cleaned up comments and + some error messages. + +2006-08-24 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/dlopen.c: Fixed segfault in dlclose on HPUX, + based on feedback from <biswatosh2001@yahoo.com>. + +2006-07-16 Alexey Melnikov <alexey.melnikov@isode.com> + * win32/common.mak: Abstracted out compiler command + line options for exception handling. + +2006-07-04 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/auth_shadow.c: Include crypt.h, so that crypt() + is defined. This fixes crash on x64 Suse where + sizeof(int) != sizeof(char *). Based on patch from + rhafer@suse.de. + +2006-06-26 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Allow for multiple qop options + from the server and require a single qop option + from the client. + +2006-05-19 Ken Murchison <murch@andrew.cmu.edu> + * Makefile.am: include INSTALL.TXT in distro + *** Ready for 2.1.22 + +2006-05-18 Ken Murchison <murch@andrew.cmu.edu> + * cmulocal/sasl2.m4: patch to compile with MIT krb5 1.4.3 + (Philip Guenther <guenther@sendmail.com>) + +2006-05-18 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in: Fixed default value in help for the + --with-authdaemond command line option (Philip Guenther). + +2006-05-17 Alexey Melnikov <alexey.melnikov@isode.com> + * NEWS: Ready for 2.1.22 + +2006-05-17 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/Makefile.am: enable pluginviewer in the default build. + +2006-04-26 Ken Murchison <murch@andrew.cmu.edu> + * lib/server.c: call do_authorization() after successful APOP + +2006-04-26 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: If neither DES nor RC4 cipher is selected, + advertise maxssf of 1 (integrity protection). + +2006-04-26 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/pluginviewer.c: Must set fully qualified domain name + in sasl_client_new, or some plugins will not be shown. + +2006-04-26 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/client.c: Replaced wrong "break" statement with + "continue" in the client side list function. + +2006-04-25 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/NTMakefile: Enable RC4 cipher in Windows build. + +2006-04-25 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Make sure that SASL packets + shorter than 16 bytes don't cause buffer overrun. + Also prevent an error report from BoundsChecker + regarding pointer being out of range. + +2006-04-25 Alexey Melnikov <alexey.melnikov@isode.com> + * win32/common.mak: Fixed bug of not setting CODEGEN + (code generation option) if STATIC is set. + +2006-04-24 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/passdss.c, plugins/srp.c: Added include files required + by OpenSSL 0.9.8 (original patch by Dan Nicholson). + +2006-04-24 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/NTMakefile: testsuite.exe doesn't depend on saslSASLDB.dll. + +2006-04-24 Alexey Melnikov <alexey.melnikov@isode.com> + * doc/windows.html: Updated Windows build instructions. + +2006-04-20 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/testsuite.c: Removed sasl_encode test which is no longer + valid due to changed in sasl_encodev. + Also properly terminated all property request lists with NULL. + +2006-04-19 Ken Murchison <murch@andrew.cmu.edu> + * saslauthd/auth_shadow.c, saslauthd/configure.in: Check for 4/5 + argument versions of getXXname_r(). + +2006-04-19 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/common.c: Andrey V. Malyshev pointed out that the SASL + context is always NULL when the default logging callback + _sasl_syslog is called. In particular this means that + the log_level configuration option is always ignored. + +2006-04-19 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in: Search for application configuration + files in /usr/lib/sasl2 by default and fall back to + /etc/sasl2 if not found. + +2006-04-19 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/digestmd5.c: Handle missing realm option from + the client as the empty string. This match the behavior + prescribed in RFC 2831. + +2006-04-19 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/Makefile.am: Enable testsaslauthd build + by default. + +2006-04-18 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslint.h, lib/common.c: Added support for spliting + big data blocks (bigger than maxbuf) into multiple SASL + packets in sasl_encodev. + +2006-04-10 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/Makefile.am: Added the pluginviewer man page. + Reordered link dependencies for saslpasswds/sasldblistusers2. + +2006-04-10 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/pluginviewer.8: Added man page for pluginviewer. + +2006-04-10 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/pluginviewer.c: Deleted unused command line parameters + and cleaned up usage output. + +2006-04-10 Alexey Melnikov <alexey.melnikov@isode.com> + * include/gai.h: Use HAVE_GETADDRINFO (instead of HAVE_GETNAMEINFO) + to protect definition of getaddrinfo(). + +2006-04-10 Alexey Melnikov <alexey.melnikov@isode.com> + * include/sasl.h: Allocated some GSSAPI specific properties + for Nico Williams (Sun) + +2006-04-10 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/common.c: Free default_plugin_path and + default_conf_path variables in sasl_done. + +2006-04-10 Alexey Melnikov <alexey.melnikov@isode.com> + * sasldb/allockey.c: Cleaned up some warnings + +2006-04-10 Alexey Melnikov <alexey.melnikov@isode.com> + * win32/include/config.h: Deleted a misleading comment + +2006-04-06 Jeffrey Teaton <jeaton@cmu.edu> + * saslauthd/auth_rimap.c: patch from Dale Sedivec to prevent + segfault when saslauth free()s returned string + * plugins/sql.c: patch from Matthew Hardin to do better + error checking for mysql_real_query + +2006-04-03 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in, plugins/NTMakefile, plugins/sasldb.c, + sasldb/db_berkeley.c, sasldb/sasldb.h: + Patch to keep BerkleyDB handle open between operations + (for performance reason). New behavior can be enabled + with --enable-keep-db-open. Original patch by Curtis King. + +2006-03-14 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/server.c: Fixed bug # 2796: load_config now + looks in all directories for the config file, + not just in the first one. + +2006-03-14 Alexey Melnikov <alexey.melnikov@isode.com> + * include/saslplug.h, lib/auxprop.c, lib/client.c + lib/server.c, utils/Makefile.am, utils/NTMakefile, + utils/pluginviewer.c [new]: + Added support for reporting information about + loaded auxprop plugins. Changed the first parameter + to sasl_server_plugin_info/sasl_client_plugin_info + to be "const char *". Added new utility for + reporting information about client and server side + authentication plugins and auxprop plugins (e.g. + supported features, methods, etc.). + +2006-03-13 Alexey Melnikov <alexey.melnikov@isode.com> + * saslauthd/Makefile.am, saslauthd/auth_httpform.c, + saslauthd/auth_httpform.h, saslauthd/configure.in, + saslauthd/mechanisms.c, saslauthd/mechanisms.h: + Added support for HTTP POST password validation + in saslauthd (patch by Joe Ammann <joe@pyx.ch>) + +2006-03-13 Alexey Melnikov <alexey.melnikov@isode.com> + * cmulocal/openldap.m4: Allow for compilation + with OpenLDAP 2.3+. + +2006-03-13 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslutil.c, utils/testsuite.c: Various + fixes to sasl_decode64: don't ignore partial + base64 data, don't allow any data after the '=' + sign, etc.). + +2006-03-13 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/saslint.h: Increase canonicalization buffer + size to 1024 bytes, as Luke Howard has reported + that 256 is too small for some certificates. + +2006-03-13 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/NTMakefile: Include Cyrus version of + getnameinfo() when compiling with Visual Studio 6, + as Windows SDK emulation is not available. + +2006-02-13 Alexey Melnikov <alexey.melnikov@isode.com> + * include/sasl.h, lib/common.c: Added sasl_set_path + function (for a more convenient way of setting + plugin and config paths. Changed the default + sasl_getpath_t/sasl_getconfpath_t callbacks to + calculate the value only once and cache it + for later use. + +2006-02-13 Alexey Melnikov <alexey.melnikov@isode.com> + * configure.in, include/sasl.h, lib/common.c, + lib/saslinit.h, lib/server.c, man/Makefile.am, + man/sasl_callbacks.3, man/sasl_getconfpath_t.3, + win32/include/config.h: Added a new sasl_getconf_t + callback for specifying where SASL configuration files + can be found. Based on patch from Artur Frysiak + <wiget@pld.org.pl> for SASL v1, updated by Gentoo + folks for SASL v2 and further modified by + Andreas Hasenack <andreas@conectiva.com.br>. + +2006-01-31 Alexey Melnikov <alexey.melnikov@isode.com> + * INSTALL, INSTALL.TXT: Renamed INSTALL to INSTALL.TXT + as the former conflicts with Windows "install" target + (and Windows file names are case-insensitive). + +2005-08-11 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/sasldb.c: Return SASL_NOUSER only if all calls to + _sasldb_putdata() return SASL_NOUSER. This prevents spurious + SASL_NOUSER errors. + +2005-07-07 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/ntlm.c: Added <openssl/md5.h> include in order to fix + building with OpenSSL 0.9.8. + +2005-05-19 Derrick Brashear <shadow@andrew.cmu.edu> + * config/libtool.m4: do proper quoting, from Andreas Winkelmann + * configure.in: clean up enable switches, from Patrick Welche + * config/sasldb.m4: fix macro names, from Andreas Winkelmann + * lib/client.c: deal with gcc4 strictness, from Steven Simon + +2005-05-16 Derrick Brashear <shadow@andrew.cmu.edu> + * configure.in, include/sasl.h, lib/Makefile.am, + plugins/Makefile.am, saslauthd/configure.in, sasldb/Makefile.am, + win32/common.mak, win32/include/config.h: 2.1.21 + * Makefile.am: fix dist-hook to run makeinit.sh in plugins/ + +2005-05-15 Derrick Brashear <shadow@andrew.cmu.edu> + * saslauthd/lak.c: leak fix from Igor Brezac + +2005-05-15 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/NTMakefile: ldapdb on Windows might depend on OpenSSL. + +2005-05-06 Derrick Brashear <shadow@andrew.cmu.edu> + * configure.in, saslauthd/auth_pam.c: detect pam header location also + where MacOS provides it, and use it there + * utils/Makefile.am: change link order for MacOS + * configure.in: provide option to disable installing MacOS SASL2 + framework + * configure.in, config/kerberos_v4.m4, config/plain.m4, + config/sasldb.m4, lib/Makefile.am, sasldb/Makefile.am, + (cmulocal/sasl2.m4): fix case where we are building + --enable-static --with-dblib=none causing automake's dependancy + stuff to screw us when we try to build files with .. in their path + +2005-04-11 Derrick Brashear <shadow@andrew.cmu.edu> + * configure.in, plugins/digestmd5.c: detect and include des.h if it + exists, otherwise assume we don't need it (Solaris 9) + +2005-04-11 Derrick Brashear <shadow@andrew.cmu.edu> + * sasldb/Makefile.am, config/sasldb.m4: work around HP-UX make's + inability to have pipes in $(shell ...) by setting + LOCAL_SASL_DB_BACKEND_STATIC at the same time as + SASL_DB_BACKEND_STATIC. + +2005-03-15 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/dlopen.c: log the reason for opendir() failure + when loading plugin. + +2005-03-08 Alexey Melnikov <alexey.melnikov@isode.com> + * man/sasl_auxprop.3, man/sasl_auxprop_getctx.3, + man/sasl_auxprop_request.3, man/sasl_canon_user_t.3, + man/sasl_client_init.3, man/sasl_client_new.3, + man/sasl_client_start.3, man/sasl_client_step.3, + man/sasl_decode.3, man/sasl_errdetail.3, man/sasl_errstring.3, + man/sasl_getpath_t.3, man/sasl_getrealm_t.3, + man/sasl_getsecret_t.3, man/sasl_server_init.3, + man/sasl_server_new.3, man/sasl_server_start.3, + man/sasl_server_step.3, man/sasl_setpass.3, + man/sasl_user_exists.3, man/sasl_verifyfile_t.3: multiple + spelling corrections from Steven Simon <steven_si@sbcglobal.net>. + +2005-03-07 Alexey Melnikov <alexey.melnikov@isode.com> + * utils/saslpasswd2.8, utils/sasldblistusers2.8: updated manpages. + +2005-03-01 Derrick Brashear <shadow@andrew.cmu.edu> + * lib/common.c: honor log level setting + +2005-02-28 Derrick Brashear <shadow@andrew.cmu.edu> + * README.ldapdb: ldapdb license info + +2005-02-25 Alexey Melnikov <alexey.melnikov@isode.com> + * include/sasl.h, lib/common.c: Added SASL_VERSION_FULL + define + +2005-02-22 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/NTMakefile, win32/common.mak: Windows build of the ldapdb + auxprop plugin + +2005-02-16 Derrick Brashear <shadow@andrew.cmu.edu> + * configure.in, doc/install.html, doc/options.html, doc/readme.html, + doc/sysadmin.html, lib/staticopen.h, plugins/Makefile.am, + plugins/ldapdb.c, plugins/makeinit.sh: pull in ldapdb auxprop + plugin, from Igor Brezac (Howard Chu's plugin) + +2005-02-14 Derrick Brashear <shadow@andrew.cmu.edu> + * saslauthd/krbtf.c: updated from CMUCS + * saslauthd/auth_krb5.c: log the krb5 error return if get_creds fails + +2005-02-01 Alexey Melnikov <alexey.melnikov@isode.com> + * win32/include/config.h: Updated to match gai.h changes. + * win32/include/config.h: added define for the OTP plugin. + +2005-01-27 Derrick Brashear <shadow@andrew.cmu.edu> + * configure.in, include/gai.h: move AI_NUMERICHOSTS definitions + to config.h because gai.h is not always included. + +2005-01-10 Derrick Brashear <shadow@andrew.cmu.edu> + * saslauthd/auth_krb5.c, saslauthd/auth_krb4.c, + saslauthd/krbtf.h (added), saslauthd/krbtf.c (added), + saslauthd/cfile.h (added), saslauthd/cfile.c (added), + saslauthd/Makefile.am: Kerberos V4/V5 alternate keytab + in saslauthd, plus common code merging (from David Eckhardt + via Dale Moore) + +2004-12-08 Alexey Melnikov <alexey.melnikov@isode.com> + * doc/windows.html: Updated as per recent build changes. + * plugins/ntlm.c: Fixed NTLM build on Windows, + as compiler was complaining about array size not being + a const. + * lib/NTMakefile, plugins/NTMakefile, win32/common.mak, + win32/include/config.h: Use native IPv6 support on Windows, + falling back to Microsoft emulation. Cleaner support + for Visual Studio 6. + +2004-11-24 Ken Murchison <ken@oceana.com> + * plugins/sql.c: squashed unused parameter warnings + +2004-11-24 Ken Murchison <ken@oceana.com> + * plugins/passdss.c: added; PASSDSS-3DES-1 implementation + * configure.in, plugins/Makefile.am, plugins/makeinit.sh: + added support for PASSDSS + * doc/draft-newman-sasl-passdss-xx.txt: added + * doc/index.html, doc/Makefile.am: added PASSDSS draft + +2004-11-19 Derrick Brashear <shadow@andrew.cmu.edu> + * saslauthd/auth_krb5.c: verify against the service we + were passed. needs to be made configurable. + +2004-11-10 Alexey Melnikov <alexey.melnikov@isode.com> + * doc/draft-burdis-cat-srp-sasl-08.txt: deleted + * doc/draft-ietf-sasl-anon-02.txt: deleted + * doc/draft-ietf-sasl-crammd5-01.txt: deleted + * doc/draft-ietf-sasl-gssapi-00.txt: deleted + * doc/draft-ietf-sasl-plain-03.txt: deleted + * doc/draft-ietf-sasl-rfc2222bis-03.txt: deleted + * doc/draft-ietf-sasl-rfc2831bis-02.txt: deleted + * doc/draft-ietf-sasl-saslprep-04.txt: deleted + * doc/draft-newman-sasl-c-api-01.txt: deleted + * doc/draft-burdis-cat-srp-sasl-xx.txt: added + * doc/draft-ietf-sasl-anon-xx.txt: added + * doc/draft-ietf-sasl-crammd5-xx.txt: added + * doc/draft-ietf-sasl-gssapi-xx.txt: added + * doc/draft-ietf-sasl-plain-xx.txt: added + * doc/draft-ietf-sasl-rfc2222bis-xx.txt: added + * doc/draft-ietf-sasl-rfc2831bis-xx.txt: added + * doc/draft-ietf-sasl-saslprep-xx.txt: added + * doc/draft-newman-sasl-c-api-xx.txt: added + * doc/index.html, doc/Makefile.am: Renamed the files + +2004-11-02 Alexey Melnikov <alexey.melnikov@isode.com> + * include/saslplug.h, lib/common.c, lib/saslint.h, + lib/client.c: Added sasl_client_plugin_info(). + +2004-10-26 Alexey Melnikov <alexey.melnikov@isode.com> + * sample/sample-client.c, sample/sample-server.c: Fixed several + 64 bit portability warnings. + * utils/testsuite.c: Fixed several 64 bit portability warnings. + * utils/saslpasswd.c: Fixed typo in an auxprop name. + * include/saslplug.h, lib/common.c, lib/saslint.h, + lib/server.c: Added sasl_server_plugin_info(). + +2004-10-24 Derrick Brashear <shadow@andrew.cmu.edu> + * lib/common.c: initialize path in case caller didn't. + +2004-10-24 Derrick Brashear <shadow@andrew.cmu.edu> + * Prep for 2.1.20 + +2004-10-19 Derrick Brashear <shadow@dementia.org> + * Makefile.am, saslauthd/Makefile.am: require automake 1.7; + prior versions require AM_CONFIG_HEADER and dislike AM_LDFLAGS + +2004-10-14 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c: portability fixes from Alexey, and squashed a + signed/unsigned warning + +2004-10-14 Alexey Melnikov <alexey.melnikov@isode.com> + * lib/NTMakefile: Don't install intermediate file libsasl.res + +2004-09-22 Derrick Brashear <shadow@andrew.cmu.edu> + * lib/common.c: don't honor SASL_PATH in setuid environment. + from Gentoo + +2004-09-08 Alexey Melnikov <alexey.melnikov@isode.com> + * plugins/cram.c, plugins/anonymous.c, plugins/login.c, + plugins/plain.c, plugins/sasldb.c: Fixed several 64 bit + portability warnings + +2004-09-02 Derrick Brashear <shadow@andrew.cmu.edu> + * plugins/kerberosv4.c: simple explanation in the code of one + possible error you might see in strange circumstances; + i should probably make openssl's des unable to be used if + mit krb5 is being used. + +2004-08-06 Derrick Brashear <shadow@andrew.cmu.edu> + * plugins/cram.c: initialize authid to null so stack garbage + is not pushed into _sasl_canon_user + +2004-07-29 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: Fix handling of client realm callback + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-07-21 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c: Memory management cleanup + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-07-15 Rob Siemborski <rjs3@andrew.cmu.edu> + * configure.in, plugins/gssapi.c: Wrap all GSS calls + in mutexes when required by the implementation. + (based on a patch by Simon Wilkinson <simon@sxw.org.uk>) + +2004-07-06 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: Fix potential buffer overflow, call + add_to_challenge in 2 more places (Alexey Melnikov + <Alexey.Melnikov@isode.com>) + * lib/server.c, lib/saslint.h, lib/common.c: don't directly + store buffers in the params structure + * plugins/gssapi.c: Fix server side maxoutbuf calculation + (Sam Hartman <hartmans@mit.edu>) + * plugins/gssapi.c: Use gss_wrap_size_limit on client side too + * Ready for 2.1.19 + +2004-07-01 Rob Siemborski <rjs3@andrew.cmu.edu> + * Prep for 2.1.19 + +2004-06-30 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/auth_rimap.c: Fix Tru64 compilation problem + * plugins/sql.c: Don't leak settings variable if init fails + * utils/testsuite.c: Update for current library + * plugins/digestmd5.c: Quoting fixes for client side + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-06-23 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: Minor bugfixes, support %R token + (Igor Brezac <igor@ypass.net>) + * plugins/otp.c: Use plugin supplied authid for mech calculations + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * lib/auxprop.c: Use getopt callback from connection context when + storing auxprops (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * plugins/otp.c, plugins/srp.c, plugins/plugin_common.c: Use correct + form of userid (user@realm) when running setpass methods + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * saslauthd/configure.in: Handle LTLIBOBJS + +2004-06-18 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/NTMakefile: Remove only recognized (generated) .rc files, + not just *.rc. This will allow for plugins with own resource files. + Also corrected spelling mistake in OPENSSL (Alexey Melnikov + <Alexey.Melnikov@isode.com>) + * lib/server.c, include/sasl.h: Support for SASL_SET_CURMECH_ONLY + flag to sasl_setpass() (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-06-16 Ken Murchison <ken@oceana.com> + * lib/server.c: use more accurate errors codes for mech_permitted() + +2004-06-16 Ken Murchison <ken@oceana.com> + * plugins/srp.c: don't used the parsed authid for calculations + (Alexey Melnikov <alexey.melnikov@isode.com>) + +2004-06-16 Rob Siemborski <rjs3@andrew.cmu.edu> + * Support for forwarding of GSSAPI credentials + (Morten Olsen <mso@medical-insight.com & + Alexey Melnikov <alexey.melnikov@isode.com>) + +2004-06-03 Rob Siemborski <rjs3@andrew.cmu.edu> + * win32/config.mak: Remove unneeded libraries + (Alexey Melnikov <alexey.melnikov@isode.com>) + +2004-06-02 Rob Siemborski <rjs3@andrew.cmu.edu> + * Spelling Fixes (selsky@columbia.edu) + +2004-05-27 Rob Siemborski <rjs3@andrew.cmu.edu> + * SQLite support (Norikatsu Shigemura <nork@ninth-nine.com>) + * SQLite support on windows (Alexey Melnikov + <Alexey.Melnikov@isode.com>) + +2004-05-25 Ken Murchison <ken@oceana.com> + * plugins/digest-md5.c: use separate global contexts for client/server + +2004-05-21 Rob Siemborski <rjs3@andrew.cmu.edu> + * configure.in, lib/Makefile.am: Better handling of -ldoor library + addition (only add it to base library, don't add -lpthread) + * saslauthd/auth_krb5.c: zero out the krb5_data structure + before use + +2004-05-20 Rob Siemborski <rjs3@andrew.cmu.edu> + * include/sasl.h, lib/common.c, lib/saslint.h, lib/server.c: + Add SASL_APPNAME to sasl_getprop/sasl_setprop for further + compatibilty with SASL C API draft + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-05-18 Ken Murchison <ken@oceana.com> + * plugins/digest-md5.c: made the global context a struct + containing the reauth_cache so we can NULL it after we free it + +2004-05-07 Ken Murchison <ken@oceana.com> + * contrib/stripplus_canonuser.patch: added + +2004-04-27 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/auth_shadow.c: Make thread-safe + (Steve Barber <steveb@cme.nist.gov>) + +2004-04-26 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/auth_krb5.c: Alternate realm support for Kerberos 5 + +2004-04-16 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c: Mac OS X fix + (Chris Ridd <chris.ridd@isode.com>) + +2004-04-14 Ken Murchison <ken@oceana.com> + * plugins/plain.c: don't include authzid in response unless + specified by client + +2004-03-29 Rob Siemborski <rjs3@andrew.cmu.edu> + * sample/server.c: Ensure that len has a value + +2004-03-25 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/saslauthd-main.c: add -r option to saslauthd for combining + user and realm into user@realm (for the userid). Based on a patch + by Jeremy Rumpf <jrumpf@heavyload.net>. + +2004-03-17 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/checkpw.c: Include errno.h when HAVE_AUTHDAEMON is defined + * doc/windows.html: Updates (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-03-16 Rob Siemborski <rjs3@andrew.cmu.edu> + * configure.in: Properly use CMU_ADD_LIBPATH_TO for pgsql and mysql + +2004-03-10 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/dlopen.c: HPUX 11 Fix (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * Add sasl_version_info() (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * Add a bunch of NTMakefile files to EXTRA_DIST in Makefile.am's + * Ready for 2.1.18 + +2004-03-08 Rob Siemborski <rjs3@andrew.cmu.edu> + * NI_WITHSCOPEID fixes (Hajimu UMEMOTO <ume@mahoroba.org>) - correct + Solaris 9 IPLOCALPORT/IPREMOTEPORT issue + +2004-02-24 Rob Siemborski <rjs3@andrew.cmu.edu> + * acinclude.m4: move to config/libtool.m4 + * saslauthd/lak.[ch]: Added filter based group membership check + (Paul Bender <pbender@qualcomm.com>, Igor Brezac <igor@ipass.net>) + +2004-02-23 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/NTMakefile: Enable DO_SRP_SETPASS on windows + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * doc/windows.html: Updates + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * win32/: Add version resource info to plugins + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * plugins/digestmd5.c: Comments and other cleanup + +2004-02-20 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/server.c, include/saslplug.h: Allow "temporary failure" + return values from mech_avail + * lib/canonusr.c, lib/server.c: Comment Nits + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * plugins/NTMakefile, plugins/plugin_common.h, + plugins/plugin_common.c, plugins/otp.c: build OTP on Windows + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-02-19 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c, sample/server.c, sample/client.c: + error checking of getnameinfo() (Paul Kranenburg <pk@cs.few.eur.nl>) + * plugins/ntlm.c: alignment and endian fixes in load_session_setup() + (Paul Kranenburg <pk@cs.few.eur.nl>) + +2004-02-18 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/NTMakefile, NTMakefile: nmake install support + for doc/ (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * plugins/digestmd5.c: Check that digest-uri is only sent once + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * utils/Makefile.am: add LIB_PGSQL to static link line + +2004-02-17 Rob Siemborski <rjs3@andrew.cmu.edu> + * win32/include/config.h: caddr_t might be already defined + elsewhere (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * lib/NTMakefile, include/saslutil.h: getopt might be already + defined elsewhere. The change will produce libsasl.dll which exports + getopt, buat a define can be used to prevent import of getopt from + libsasl.dll. (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-02-16 Rob Siemborski <rjs3@andrew.cmu.edu> + * configure.in: Remove deprecated AC_PROG_RANLIB, CMU_PROG_LIBTOOL + (Patrick Welche <prlw1@newn.cam.ac.uk>) + * lib/dlopen.c: OpenBSD ELF patch (J.C. Roberts) + +2004-02-06 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/NTMakefile, utils/NTMakefile: fix "clean" target + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * General winsock.h -> winsock2.h conversion + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * plugins/plugin_common.h: add extern "C" wrapper + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-01-23 Rob Siemborski <rjs3@andrew.cmu.edu> + * Remove "experimental" designation from saslauthd/ldap + * Correct handling of sasl_setpass errors when no + mechanisms implement the setpass interface + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2004-01-20 Rob Siemborski <rjs3@andrew.cmu.edu> + * configure.in: minor sql nit (Edward Rudd <eddie@omegaware.com>) + * lib/staticopen.h: MYSQL should be SQL + (Edward Rudd <eddie@omegaware.com>) + +2004-01-12 Rob Siemborski <rjs3@andrew.cmu.edu> + * win32/include/config.h: fix VC++ 6.0 compiles + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * configure.in: Correct use of AC_LIBOBJ, quote macro names + defined by AC_DEFUN, Use enable_shared to determine whether + to enable the shared plugin. + (Maciej W. Rozycki <macro@ds2.pg.gda.pl>) + * plugins/srp.c: Fix typos + (Maciej W. Rozycki <macro@ds2.pg.gda.pl>) + * saslauthd/configure.in: Correct use of AC_LIBOBJ + (Maciej W. Rozycki <macro@ds2.pg.gda.pl>) + +2004-01-08 Ken Murchison <ken@oceana.com> + * plugins/sql.c: better error logging + +2004-01-07 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/checkpw.c & others: Support for Courier-IMAP authdaemond + use during password verification (Leandro Santi + <lesanti@uolsinectis.com.ar>) + +2003-12-30 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: Fix NULL pointer dereference + (Simon Brady <simon.brady@otago.ac.nz>) + * saslauthd/lak.c, lak.h, LDAP_SASLAUTHD: Improved retry handler, + Improved logging/debug messages, Fixed String checks, config + option changes (Igor Brezac <igor@ipass.net>) + +2003-12-22 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: Fix memory leak + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-12-18 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/plugin_common.c: Fix handling of blob unwrapping + in _plug_decode + * lib/checkpw.c: Fix some file descriptor leaks during failures + in the saslauthd code. + +2003-12-15 Rob Siemborksi <rjs3@andrew.cmu.edu> + * utils/saslauthd.c: Fix Typo + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * plugins/plugin_common.c: Fix potential memory leak + * lib/external.c: Limit size of authzids in EXTERNAL + * plugins/gssapi.c: Pre-init some variables + * lib/cram.c: Detect possible buffer overrun + * lib/checkpw.c: Post-fence bug + (Leandro Santi <lesanti@uolsinectis.com.ar>) + +2003-12-12 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: assign null to free + variables (Juan Felipe Garcia <fgc@usal.es>) + * saslauthd/lak.c: Improve retry when ldap connection is reset + (1st pass) (Igor Brezac <igor@ipass.net>) + +2003-12-11 Rolf Braun <rbraun@andrew.cmu.edu> + * Several MacOS X Fixes + +2003-12-06 Ken Murchison <ken@oceana.com> + * lib/checkpw.c, lib/server.c, + plugins/cram.c, plugins/digestmd5.c, plugins/ntlm.c, + plugins/otp.c, plugins/srp.c: erase the plaintext password + property from the context when we're done with it + +2003-12-01 Ken Murchison <ken@oceana.com> + * doc/draft-ietf-sasl-crammd5-01.txt: added + * doc/draft-ietf-sasl-gssapi-00.txt: added + * doc/draft-ietf-sasl-plain-03.txt: added + * doc/draft-ietf-sasl-rfc2222bis-03.txt: added + * doc/draft-ietf-sasl-saslprep-04.txt: added + * doc/draft-ietf-sasl-crammd5-00.txt: deleted + * doc/draft-ietf-cat-sasl-gssapi-05.txt: deleted + * doc/draft-ietf-sasl-plain-02.txt: deleted + * doc/draft-ietf-sasl-rfc2222bis-02.txt: deleted + * doc/draft-ietf-sasl-saslprep-03.txt: deleted + * doc/index.html, doc/Makefile.am: updated to latest version of + SASL drafts + +2003-12-01 Rob Siemborski <rjs3@andrew.cmu.edu> + * Fix build nit in IRIX. + * Actual 2.1.17 release. + +2003-11-28 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.17 + +2003-11-19 Rob Siemborski <rjs3@andrew.cmu.edu> + * config/kerberos_v4.m4: Disable KERBEROS_V4 support by default + +2003-11-14 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/server.c: do authorization callback in sasl_checkpass() + (Chris Newman <chris.newman@sun.com>) + +2003-11-11 Ken Murchison <ken@oceana.com> + * lib/client.c: allow serverFDQN to be NULL in sasl_client_new() + * plugins/digestmd5.c, gssapi.c: require that we have serverFQDN + for the client side of the plugin + +2003-11-07 Rob Siemborski <rjs3@andrew.cmu.edu> + * --with-gss_impl configure option + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-11-06 Rob Siemborski <rjs3@andrew.cmu.edu> + * nmake install support for Win32 + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-11-03 Ken Murchison <ken@oceana.com> + * include/saslplug.h, lib/server.c, plugins/cram.c, + plugins/digestmd5.c, plugins/ntlm.c, plugins/otp.c, + plugins/srp.c: return SASL_TRANS to the application where + appropriate (auto_transition enabled with writable auxprop) + +2003-10-30 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: OpenLDAP 2.0 Compatability Fix + (Igor Brezac <igor@ypass.net>) + * saslauthd/ipc_unix.c: Fix buglet of not using saved errno + value (Jeremy Rumpf <jrumpf@heavyload.net>) + +2003-10-20 Rob Siemborski <rjs3@andrew.cmu.edu> + * Win64 warning squashing (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * GSSAPI cleanups and fixes (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-10-14 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.16-BETA + +2003-10-08 Rob Siemborski <rjs3@andrew.cmu.edu> + * Support for autoconf 2.57, automake 1.7 + * Minor m4 quoting fixes (Patrick Welche <prlw1@cam.ac.uk>) + +2003-10-07 Ken Murchison <ken@oceana.com> + * plugins/sql.c: removed sql_delete - don't DELETE rows from the + table, just set the properties to NULL; + fix a stupid logic error in my PgSQL changes + * doc/options.html: removed sql_delete option; clarifications + * doc/install.html: note that we require PostgreSQL v7.2+ + +2003-10-06 Ken Murchison <ken@oceana.com> + * plugins/sql.c: use the correct propctx in sql_auxprop_store() + +2003-10-06 Maya Nigrosh <mnigrosh@andrew.cmu.edu> + * plugins/sql.c: tiny bugfix to begin pgsql transactions + +2003-10-04 Ken Murchison <ken@oceana.com> + * plugins/sql.c: only do a txn when we have a property to fetch; + _pgsql_open() cleanup/fixes; more intelligient sql_usessl parsing; + require sql_select option + * doc/options.html: reorganized SQL option descriptions + +2003-10-03 Rob Siemborski <rjs3@andrew.cmu.edu> + * sasldb/allockey.c, sasldb/sasldb.h, utils/sasldblistusers.c: + Add enumeration capability to the sasldb API + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-10-02 Ken Murchison <ken@oceana.com> + * plugins/sql.c: changed abstraction layer for transactions + +2003-10-01 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/: Documentation Update + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * plugins/NTMakefile, plugins/srp.c: Win32 SRP Support + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-30 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: Clean up some warnings + * lib/canonusr.c, win32/include/config.h, win32/common.mak, + include/saslplug.h: Minor Cleanup + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * utils/NTMakefile, utils/sasldblistusers.c, utils/saslpasswd.c: + Add version options to command line utilities + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-29 Ken Murchison <ken@oceana.com> + * plugins/sql.c, doc/options.html: added sql_update and sql_delete + for a complete auxprop_store() implementation; logic cleanup + +2003-09-25 Rob Siemborski <rjs3@andrew.cmu.edu> + * utils/saslpasswd.c: Win32 perror() related patch + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-25 Ken Murchison <ken@oceana.com> + * plugins/sql.c: renamed sql_statement to sql_select, + cleanup and bugfixes + +2003-09-23 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/gssapi.html: Misc updates + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * lib/Makefile.am, plugins/Makefile.am, saslauthd/Makefile.am, + sasldb/Makefile.am: Cleanup INCLUDES for different build + directories. (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-23 Maya Nigrosh <mnigrosh@andrew.cmu.edu> + * plugins/sql.c: put transaction handling around the entirety of + the queries, and not just per-property; return the result status + of bad postgres tuples + +2003-09-22 Maya Nigrosh <mnigrosh@andrew.cmu.edu> + * plugins/sql.c: added semicolon at the end of each sql statement + +2003-09-19 Maya Nigrosh <mnigrosh@andrew.cmu.edu> + * plugins/sql.c: moved transaction handling to a more useful place, + minor bugfixes + +2003-09-18 Ken Murchison <ken@oceana.com> + * lib/server.c: log a message when no password change is attempted + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-17 Ken Murchison <ken@oceana.com> + * plugins/sql.c: misc fixes from Patrick Welche <prlw1@newn.cam.ac.uk> + +2003-09-16 Ken Murchison <ken@oceana.com> + * doc/mechanisms.html: updated to latest versions of LOGIN and + SRP drafts + +2003-09-15 Ken Murchison <ken@oceana.com> + * doc/draft-ietf-sasl-rfc2222bis-02.txt: added + * doc/draft-ietf-sasl-rfc2222bis-01.txt: deleted + * doc/index.html, doc/Makefile.am: updated to latest version of + SASL draft + +2003-09-14 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c, plugins/plugin_common.[ch]: Win32 support + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-12 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/sql.c: Log errors on connect failures + (based on patch from Bruce M Simpson <bms@spc.org>) + * plugins/NTMakefile: Add support for GSSAPI=CyberSafe + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-10 Maya Nigrosh <mnigrosh@andrew.cmu.edu> + * plugins/sql.c: created generic sql store function, added + transaction handling to sql statements + * doc/options.html: put pretty new options in the documentation + +2003-09-10 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c, win32/config.mak, sample/: Win32 Fixes + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-09 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/NTMakefile: Minor nit + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-09 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c: use retry_read() instead of just read() + * lib/checkpw.c, plugins/ntlm.c, saslauthd/utils.c: + squash signed/unsigned warning + +2003-09-08 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c: fix byte-alignment and password handling problems + +2003-09-03 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/checkpw.c: Check return value of door_call + (Gary Mills <mills@cc.umanitoba.ca>) + * saslauthd/ipc_doors.c: Implement thread limiting, + minor cleanup and error checking + (Gary Mills <mills@cc.umanitoba.ca>) + * plugins/digestmd5.c: Fix minor interop issues, limit maxbuf + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-09-02 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c, doc/options.html: added support for NTLMv2 responses; + fixed potential buffer overflow + +2003-09-02 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/common.c, lib/server.c, lib/NTMakefile, include/md5.h: + more windows compatibility + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * plugins/NTMakefile: Add ability to build NTLM plugin under + Win32 (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * utils/NTMakefile: Add ability to build testsuite + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * saslauthd/lak.c: Minor error message fix + (Igor Brezac <igor@ypass.net>) + +2003-08-29 Ken Murchison <ken@oceana.com> + * doc/draft-murchison-sasl-login-00.txt: added + * doc/draft-sasl-login.txt: deleted + * doc/index.html, doc/Makefile.am: updated to "official" LOGIN draft + +2003-08-29 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c: properly compute GSSAPI MAXOUTBUF + (Paul Turgyan <pturgyan@umich.edu>) + * Further Win32 cleanup + HIER_DELIMITER usage + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + +2003-08-28 Rob Siemborski <rjs3@andrew.cmu.edu> + * include/md5.h, lib/md5.c: Misc cleanup + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * utils/sasldblistusers.c: UI Cleanup, Win32 support + (Alexey Melnikov <Alexey.Melnikov@isode.com>) + * acconfig.h: add HIER_DELIMITER + +2003-08-27 Ken Murchison <ken@oceana.com> + * plugins/digestmd5.c: handle OpenSSL 0.9.7+ w/o old DES support + +2003-08-26 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c: only send one NT/LM response to server + (NT preferred); don't use canonified authid when proxying + +2003-08-24 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c, doc/options.html: allow NTLM authentication to + be optionally proxied to an NT server (ntlm_server option) + +2003-08-24 Ken Murchison <ken@oceana.com> + * lib/common.c: added support for unsigned int types in _sasl_log() + +2003-08-18 Rob Siemborski <rjs3@andrew.cmu.edu> + * Improvements in Win32 build system from Alexey Melnikov + <Alexey.Melnikov@isode.com> + +2003-08-14 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/*: Massive documentation updates. + +2003-08-13 Ken Murchison <ken@oceana.com> + * doc/index.html: added reference to a CIFS (SMB/NTLM) document + +2003-08-12 Ken Murchison <ken@oceana.com> + * doc/index.html: added reference to a good NTLM document + +2003-07-29 Ken Murchison <ken@oceana.com> + * plugins/cram.c: don't truncate long secrets to 64 bytes on the + client-side of CRAM-MD5 (jiang_xiong@yahoo.com) + +2003-07-28 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c: another missed pointer init + (Will Fiveash <william.fiveash@sun.com>) + +2003-07-26 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/server.c: Missed pointer initialization fix + ("Dave Cridland [Home]" <dave@cridland.net>) + +2003-07-26 Ken Murchison <ken@oceana.com> + * plugins/digestmd5.c: merged privacy and integrity security layer + code and removed use of tmp buffers for security layer + +2003-07-25 Ken Murchison <ken@oceana.com> + * plugins/srp.c: removed use of tmp buffer for security layer; + don't make a big buffer out of iovecs when encoding + * lib/server.c, plugins/login.c, plugins/plain.c: better handling + of auto_transition -- doesn't try to transition from auxprop to + auxprop + +2003-07-25 Rob Siemborski <rjs3@andrew.cmu.edu> + * configure.in: Fix up some mysql/pgsql detection + * plugins/gssapi.c: improved error reporting + (William Fiveash <William.Fiveash@sun.com>) + * cmulocal/sasl2.m4, saslauthd/mechanisms.h: Improved + GSSAPI detection (don't default to MIT, require HAVE_KRB5_H + for the kerberos5 saslauthd module) + (Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>) + +2003-07-24 Ken Murchison <ken@oceana.com> + * plugins/srp.c: updated security layer code to be closer to draft -08 + +2003-07-23 Rob Siemborksi <rjs3@andrew.cmu.edu> + * saslauthd/utils.[ch], saslauthd/configure.in: Detect/replace + strlcpy and strlcat (based on ideas from + Igor Brezac <igor@ipass.net>) + +2003-07-22 Ken Murchison <ken@oceana.com> + * plugins/digestmd5.c, plugins/gssapi.c, plugins/kerberos4.c, + plugins/plugin_common.[ch]: moved encoded packet buffering into + _plug_decode() + +2003-07-21 Ken Murchison <ken@oceana.com> + * plugins/srp.c: updated auth code to draft -08 (layers still need + to be updated) + * configure.in, plugins/srp.c: use auxprop_store() instead of + direct sasldb access + +2003-07-21 Rob Siemborski <rjs3@andrew.cmu.edu> + * configure.in: add runpath information for MySQL and Postgres; + better behavior for the interaction of --enable-sql and + --with-mysql / --with-pgsql + * saslauthd/lak.[ch]: %d to be derived from %u if it can be, + otherwise use %r (to account for the recent change in the + core library). Add ldap_default_realm parameter + (Igor Brezac <igor@ipass.net>) + +2003-07-18 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: Client side of digest md5 doesn't + have quotes around its cypher= directive (Bug 2113). + * saslauthd/lak.[ch]: support for ldap sasl binds, + support for tls (Igor Brezac <igor@ipass.net>) + +2003-07-17 Ken Murchison <ken@oceana.com> + * include/sasl.h, include/saslplug.h, + * lib/auxprop.c, lib/common.c, lib/server.c, plugins/sasldb.c: + implemented writable auxprops + * configure.in, plugins/otp.c, utils/saslpasswd: use + auxprop_store() instead of direct sasldb access + * doc/options.html, lib/server.c: implemented 'noplain' option for + auto_transition + +2003-07-17 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/config.c: Remove sasl_config_getint and sasl_config_getswitch + because they are unused and confusing + * lib/checkpw.c: Correctly split realm from username in + saslauthd_verify_password + +2003-07-15 Ken Murchison <ken@oceana.com> + * plugins/sql.c, doc/options.html: added sql_usessl option + +2003-07-15 Ken Murchison <ken@oceana.com> + * plugins/mysql.c: deleted + * plugins/sql.c: added + * acconfig.h, configure.in, + doc/components.html, doc/options.html, doc/sysadmin.html, + plugins/Makefile.am, plugins/makeinit.sh: deprecated MySQL plugin + in favor of a new generic SQL plugin (currently supports MySQL and + PostgreSQL) + +2003-07-15 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.15 + +2003-07-03 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/components.html: added in the hopes that this gives a better + description of how all the components interact + +2003-07-02 Ken Murchison <ken@oceana.com> + * doc/draft-ietf-sasl-anon-02.txt: added + * doc/draft-ietf-sasl-plain-02.txt: added + * doc/draft-ietf-sasl-saslprep-03.txt: added + * doc/draft-ietf-sasl-anon-01.txt: deleted + * doc/draft-ietf-sasl-plain-01.txt: deleted + * doc/index.html, doc/Makefile.am: updated to latest versions of + PLAIN, ANONYMOUS, SASLprep drafts + +2003-07-02 Rob Siemborski <rjs3@andrew.cmu.edu> + * acconfig.h, cmulocal/sasl2.m4, plugins/gssapi.c: + Properly detect HAVE_GSS_C_NT_USER_NAME + (Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>) + +2003-07-01 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/kerberos4.c: Fix some maxoutbuf handling issues + +2003-07-01 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/mysql.c: Check return value of mysql_init + (Ivan Kelly <ivan@ivankelly.net>) + +2003-07-01 Ken Murchison <ken@oceana.com> + * doc/draft-burdis-cat-srp-sasl-08.txt: added + * doc/draft-ietf-sasl-rfc2222bis-01.txt: added + * doc/draft-ietf-sasl-rfc2831bis-02.txt: added + * doc/draft-burdis-cat-srp-sasl-06.txt: deleted + * doc/draft-ietf-sasl-rfc2222bis-00.txt: deleted + * doc/draft-ietf-sasl-rfc2831bis-01.txt: deleted + * doc/index.html, doc/Makefile.am: updated to latest versions of + SASL, SRP, DIGEST-MD5 drafts + +2003-06-30 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/mysql.c: Call mysql_init() too + (Hajimu UMEMOTO <ume@mahoroba.org>) + +2003-06-28 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/sysadmin.html: Add more text about how to use realms. + +2003-06-27 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.14 + +2003-06-11 Rolf Braun <rbraun@andrew.cmu.edu> + * config/kerberos_v4.m4: + fix fallback to -lkrb4 when --enable-krb4 is specified + * config/ltconfig: + * config/ltmain.sh: + make the darwin libtool work on OS X v10.2 + (bash/zsh shell syntax, and don't link bundles with extra args) + * dlcompat-20010505/dlopen.c: back out bogus delimiter change + * doc/macosx.html: update for 10.2 and add known problems section + * mac/osx_cfm_glue/cfmglue.c: fix sasl_done followed by client_init + +2003-06-11 Rob Siemborski <rjs3@andrew.cmu.edu> + * man/sasl_client_new.3, man/sasl_server_new.3: + Security flags don't belong here, connection flags do. + +2003-06-10 Ken Murchison <ken@oceana.com> + * doc/draft-ietf-sasl-crammd5-00.txt: added + * doc/draft-nerenberg-sasl-crammd5-03.txt: deleted + * doc/index.html, doc/Makefile.am: updated to WG version of + CRAM-MD5 draft + +2003-05-30 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c: If we get an empty output token back + from gss_accept_sec_context, return + an empty string to transmit to the client. + +2003-05-30 Ken Murchison <ken@oceana.com> + * doc/draft-ietf-sasl-rfc2831bis-01.txt: added + * doc/draft-ietf-sasl-rfc2831bis-00.txt: deleted + * doc/index.html, doc/Makefile.am: updated to latest version of + DIGEST-MD5 draft + +2003-05-28 Ken Murchison <ken@oceana.com> + * doc/draft-ietf-sasl-anon-01.txt: added + * doc/draft-ietf-sasl-plain-01.txt: added + * doc/draft-ietf-sasl-rfc2222bis-00.txt: added + * doc/draft-ietf-sasl-anon-00.txt: deleted + * doc/draft-ietf-sasl-plain-00.txt: deleted + * doc/draft-myers-saslrev-02.txt: deleted + * doc/index.html, doc/Makefile.am: updated to latest versions of + SASL, PLAIN, ANONYMOUS drafts + +2003-05-21 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/ipc_unix.c: Accept File Descriptor Locking + Fixes (found by Leena Heino <Leena.Heino@uta.fi>) + * saslauthd/cache.c: Similar fixes + (Jeremy Rumpf <jrumpf@heavyload.net>) + +2003-05-15 Rob Siemborski <rjs3@andrew.cmu.edu> + * configure.in: Actually listen to --disable-java + (Maciej W. Rozycki <macro@ds2.pg.gda.pl>) + * saslauthd/saslauthd-main.h: Increase listen backlog to + match Cyrus master process (Igor Brezac <igor@ipass.net>) + +2003-05-14 Rob Siemborski <rjs3@andrew.cmu.edu> + * config/kerberos_v4.m4: Minor nit + (Carlos Velasco <carlosev@newipnet.com>) + * plugins/gssapi.c: Use GSS_C_NT_USER_NAME + to work around Solaris 8/9 libgss bug. + (gssapi_client_mech_step): Pass GSS_C_NO_BUFFER to first + invocation of gss_init_sec_context to work around Solaris 8/9 + mech_krb5 bug. (Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>) + * cmulocal/sasl2.m4: Check for Sun SEAM GSS-API implementation + (Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>) + * saslauthd/configure.in: Check for krb5.h. Don't define if GSSAPI + is present. (Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>) + * saslauthd/mechanisms.h: Test for HAVE_KRB5_H instead of HAVE_GSSAPI_H + to activate AUTH_KRB5. (Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>) + * plugins/mysql.c: Use mysql_real_connect() instead of mysql_connect() + (Petri Riihikallio <Petri.Riihikallio@Metis.fi>) + * saslauthd/: Misc ANSI C cleanups (Jeremy Rumpf <jrumpf@heavyload.net>) + +2003-05-13 Rob Siemborski <rjs3@andrew.cmu.edu> + * config/sasldb.m4, utils/Makefile.am: fix installation of man + pages that are homed in the utils/ directory + * include/*.h: Add extern "C" blocks for C++ compiles + +2003-05-06 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/saslauthd-main.c: misc spelling and UI cleanups + +2003-04-16 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/saslauthd-main.c: Don't set the auth mech until + all options have been processed. (Peter Stamfest <peter@stamfest.at>) + * lib/client.c, lib/common.c, lib/saslint.h, lib/server.c: Do + reference counting of the number of times sasl has been inited/doned. + +2003-04-15 Rob Siemborski <rjs3@andrew.cmu.edu> + * config/ltmain.sh: fix some portability problems in the use of expr + (Oliver Eikemeier <eikemeier@fillmore-labs.com>) + +2003-04-14 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.13 + +2003-04-08 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/external.c, lib/server.c: use mech_avail to disable + EXTERNAL instead of special casing it (Chris Newman + <Chris.Newman@Sun.COM>) + +2003-03-31 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/ipc_unix.c, saslauthd/saslauthd-main.c, + saslauthd/saslauthd-main.h: use the pidfile locking from + the Cyrus IMAPd master process (implemented for saslauthd by + Igor Brezac <igor@ipass.net>) + * configure.in, acconfig.h: Add configure option to set what + we use for /dev/random + +2003-03-28 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/: Unify the source files so that the IPC methods + are broken out into a separate API. Cacheing of authentication + credentials is also available as a command-line option. + Other changes include: Remove Time of Day Flag, omit + SO_REUSEADDR on AF_UNIX sockets, make using the accept-socket + locking runtime configurable, and misc other cleanup. + (Jeremy Rumpf <jrumpf@heavyload.net>) + +2003-03-26 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/plain.c: Defend against memory leak on canon_user + failure (Chris Newman <chris.newman@sun.com>) + +2003-03-19 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/auxprop.c, lib/checkpw.c, lib/common.c, lib/saslutil.c, + lib/server.c: Assorted minor fixes from Sun Microsystems + (provided by Chris Newman <chris.newman@sun.com>) + +2003-03-13 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: Fix a memset length. (Igor Brezac <igor@ipass.net>) + +2003-03-06 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: fix parity of digest-uri test + * lib/client.c, common.c, saslint.h, server.c: Pass global + callbacks to global utils structure + (Howard Chu <hyc@highlandsun.com>) + * saslauthd/auth_krb5.c: Fix memory/file descriptor leak + in krb5 authentication (Jonathen Chen <jon@spock.org>) + * saslauthd/lak.c, lak.h, LDAP_SASLAUTHD: Remove ldap_cache + code, and rename MAX() to LAK_MAX() + +2003-02-20 Ken Murchison <ken@oceana.com> + * doc/draft-ietf-sasl-rfc2831bis-00.txt: added + * doc/draft-melnikov-rfc2831bis-02.txt: deleted + * doc/draft-newman-sasl-c-api-01.txt: added + * doc/draft-newman-sasl-c-api-00.txt: deleted + * doc/index.html: updated to WG version of DIGEST-MD5 draft, + updated to latest C API draft + * doc/Makefile.am: updated to WG version of DIGEST-MD5 draft, + updated to latest C API draft + +2003-02-12 Lawrence Greenfield <leg+@andrew.cmu.edu> + * plugins/digestmd5.c: verify the service component of digest-uri + +2003-02-11 Ken Murchison <ken@oceana.com> + * doc/draft-ietf-sasl-anon-00.txt: added + * doc/draft-ietf-sasl-plain-00.txt: added + * doc/draft-zeilenga-sasl-anon-01.txt: deleted + * doc/draft-zeilenga-sasl-plain-01.txt: deleted + * doc/index.html: updated to WG versions of ANONYMOUS, PLAIN drafts + +2003-02-03 Rob Siemborski <rjs3@andrew.cmu.edu> + * cmulocal/sasl2.m4: Don't use -ldes to check for Heimdal + * saslauthd/auth_krb4.c, saslauthd/auth_shadow.c, + saslauthd/auth_getpwent.c, lib/kerberos4.c: + Smarter checking of #includs for des.h + (Mark Keasling <mark@air.co.jp>) + * saslauthd/testsaslauthd.c, saslauthd/saslauthd-doors.c: + retry_read() should use a char * buffer not a void * + buffer (Mark Keasling <mark@air.co.jp>) + * cmulocal/berkdb.m4: Set CPPFLAGS around tests + (based on patch from Leena Heino <Leena.Heino@uta.fi>) + * config/sasldb.m4: Actually use results of Berkeley DB tests + (Leena Heino <Leena.Heino@uta.fi>) + * Ready for 2.1.12 + +2003-01-31 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.11 + * utils/Makefile.am: Ensure that dbconverter-2 can see the sasldb + include directory. + +2003-01-29 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: Fix a situation where the realm wasn't + being set for the client context, causing a segfault + * config/kerberos_v4.m4: first check des_* then check DES_* + during OpenSSL tests (based on ideas from + Leena Heino <Leena.Heino@uta.fi>) + +2003-01-28 Rob Siemborski <rjs3@andrew.cmu.edu> + * config/sasldb.m4: Don't build sasldb plugin if compiling + --with-dblib=none, since it will only fail to load anyway. + +2003-01-27 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/configure.in: use CMU_ADD_LIBPATH for LDAP support + (Simon Brady <simon.brady@otago.ac.nz>) + +2003-01-23 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/acconfig.h: protect file from being included more than + once (reported by Jeremy Rumpf <jrumpf@heavyload.net>) + * saslauthd/configure.in, configure.in: Move OpenSSL detection into + cmulocal, detect openssl for use with lak.c + +2003-01-21 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c: only _require_ one response (LM and/or NT), not both + +2003-01-09 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c, saslauthd/lak.h: Add the fastbind auth method + (Simon Brady <simon.brady@otago.ac.nz>) + +2003-01-01 Ken Murchison <ken@oceana.com> + * saslauthd/configure.in, saslauthd/Makefile.am: don't make + -lcrypt dependent upon --enable-plain + +2002-12-11 Ken Murchison <ken@oceana.com> + * plugins/otp.c: set SASL_FEAT_ALLOWS_PROXY on client side + +2002-12-10 Ken Murchison <ken@oceana.com> + * plugins/otp.c: explicitly #include <openssl/md5.h> to resolve + OpenBSD/OpenSSL cruftiness + +2002-12-10 Rob Siemborksi <rjs3@andrew.cmu.edu> + * saslauthd/saslauthd-doors.c: Fix a potential memory leak when + we call door_return() + +2002-12-09 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/auxprop.c: Correct leak in prop_clear, also update list_end + in prop_request. + * doc/options.html: Update use of saslauthd_path to be correct + +2002-12-06 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.10 + +2002-12-05 Larry Greenfield <leg@andrew.cmu.edu> + * plugins/digestmd5.c: DES key fixes. stupid DES libraries want + the key in the stupid DES parity format. + * plugins/digestmd5.c: refactored some of the cipher code so that + there isn't RC4 state around when we're using DES and vice versa + +2002-12-05 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: Allocate a large enough buffer to account for + a completely escaped username. (lak_escape and lak_filter) + * lib/common.c: Ensure there is enough space for the trailing \0 + in _sasl_log + +2002-12-04 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/canonusr.c: Check for potential buffer overflow + +2002-12-03 Ken Murchison <ken@oceana.com> + * plugins/digestmd5.c: major fast reauth rewrite, mech_step cleanup + * doc/options.html: server-side reauth is disabled by default + +2002-11-24 Ken Murchison <ken@oceana.com> + * plugins/login.c: allow authid to be passed in initial response + * doc/draft-sasl-login.txt, doc/mechanisms.html: + documentation updates re: initial response + +2002-11-07 Ken Murchison <ken@oceana.com> + * doc/draft-nerenberg-sasl-crammd5-03.txt: added + * doc/draft-nerenberg-sasl-crammd5-02.txt: deleted + * doc/draft-zeilenga-sasl-anon-01.txt: added + * doc/draft-zeilenga-sasl-anon-00.txt: deleted + * doc/draft-zeilenga-sasl-plain-01.txt: added + * doc/draft-zeilenga-sasl-plain-00.txt: deleted + * doc/index.html: updated to latest CRAM-MD5, ANONYMOUS, PLAIN drafts + +2002-11-01 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/kerberos4.c: Make at most 1 canon_user call, not two. + (Howard Chu <hyc@highlandsun.com>) + +2002-10-25 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: minor cleanups + +2002-10-24 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: fix problem where saslauthd stops LDAP + authentications when ldap_auth_method is bind. + (Igor Brezac <igor@ypass.net>) + * doc/sysadmin.html, doc/options.html, saslauthd/saslauthd.mdoc: + documentation updates re: saslauthd mux path + +2002-10-23 Ken Murchison <ken@oceana.com> + * lib/external.c: added SASL_SEC_NOANONYMOUS to client side + (Howard Chu, <hyc@highlandsun.com>) + +2002-10-21 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c: NTLM probably doesn't offer perfect forward secrecy + * doc/mechanisms: added table of properties/features + +2002-10-20 Ken Murchison <ken@oceana.com> + * saslauthd/lak.ch: consolidated hashed password checking code + +2002-10-18 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.[ch], saslauthd/auth_ldap.c: + Code cleanup, now support {SHA}, {SSHA}, {MD5}, and {SMD5} hashes, + misc other cleanup. (Igor Brezac <igor@ypass.net> and + Thomas Lussnig <thomas.lussnig@bewegungsmelder.de>) + +2002-10-17 Ken Murchison <ken@oceana.com> + * doc/draft-melnikov-rfc2831bis-02.txt: added + * doc/draft-melnikov-rfc2831bis-01.txt: deleted + * doc/index.html: updated to latest RFC 2831bis draft + +2002-10-11 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/Makefile.am: add missing staticopen.h to EXTRA_DIST, + fix some dependencies + * Ready for 2.1.9 + +2002-10-10 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.8 + +2002-10-09 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/client.c: Allow plaintext mechanisms under an external security + layer. + +2002-10-07 Rob Siemborski <rjs3@andrew.cmu.edu> + * sample/server.c: Fix some IPV6 defines + (Marshall Rose <mrose@dbc.mtview.ca.us>) + +2002-10-02 Ken Murchison <ken@oceana.com> + * lib/checkpw.c: return SASL_NOUSER when we can't find APOP secret + * lib/server.c: plug APOP memory leak and consolidate canonification + * configure.in: force the use of a cache file + (Carlos Velasco <carlosev@newipnet.com>) + +2002-10-02 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/checkpw.c: Fix some misuses of sasl_seterror + (Martin Exler <m.exler@gmx.at>) + +2002-09-24 Rob Siemborski <rjs3@andrew.cmu.edu> + * config/sasl2.m4, saslauthd/Makefile.am: GSSAPI doesn't need + to link ndbm. Also cleanup some sasldb linking in saslauthd. + +2002-09-23 Rob Siemborski <rjs3@andrew.cmu.edu> + * config/kerberos_v4.m4: Don't compile with kerberos unless we + have both the libs and the headers (Carlos Velasco + <carlosv@newipnet.com>) + +2002-09-19 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c: endinaness corrections + * sasldb/db_berkeley.c, utils/dbconverter-2.c: Berkley DB 4.1 + support (Mika Iisakkila <mika.iisakkila@pingrid.fi>) + +2002-09-19 Ken Murchison <ken@oceana.com> + * plugins/plugin_common.[ch]: make SASL_CB_USER and result optional + * plugins/anonymous.c: use SASL_CB_USER for fetching trace info, + don't require SASL_CB_AUTHNAME + * plugins/gssapi.c, plugins/kerberos.c: don't require SASL_CB_USER + * lib/external.c: define SASL_FEAT_ALLOWS_PROXY for this mechanism, + don't require SASL_CB_USER + +2002-09-18 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/srp.c, plugins/kerberos4.c: correct maxoutbuf handling + * plugins/digestmd5.c: correct maxoutbuf handling, actually + send maxbuf to the remote. + * lib/common.c: sanity check security properties + +2002-09-17 Ken Murchison <ken@oceana.com> + * plugins/ntlm.c: home-grown client/server NTLM implementation + * configure.in: NTLM depends on OpenSSL libcrypto + * doc/sysadmin.html: added NTLM blurb + +2002-09-16 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/canonusr.c: don't index begin_u with -1 + (Randy Kunkee <randy@randallkunkee.com>) + * doc/sysadmin.html: cleanup + * utils/saslpasswd.c: don't exit with -SASL_FAIL + * saslauthd/saslauthd-unix.c: use a char* instead of a void* in + retry_read + +2002-09-12 Ken Murchison <ken@oceana.com> + * lib/common.c: NULL outbuf if we get no output from sasl_decode() + +2002-09-11 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/mysql.c: Actually loop through the potential servers + properly (Seow Kok Heng <kokheng@jhs.com.sg>) + * acinclude.m4: Added copy of the correct libtool macros as + acinclude.m4 + * configure.in: fix for gcc 3.x + (Carlos Velasco <carlosev@newipnet.com>) + +2002-09-10 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/server.c: Better handling of add_plugin failures + +2002-09-10 Ken Murchison <ken@oceana.com> + * acconfig.h, configure.in: enable/disable NTLM + * lib/staticopen.h, plugins/Makefile.am, makeinit.sh, ntlm.c: + added NTLM support (client-side only) + +2002-09-07 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/configure.in, saslauthd/Makefile.am: don't + do configure substitutions for the saslauthd_SOURCES variable + (Carlos Velasco <carlosev@newipnet.com>) + +2002-09-05 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/os390.html: added + * doc/index.html: referenced os390.html and macosx.html + * lib/Makefile.am: better handling of plugin_common + +2002-09-04 Rob Siemborski <rjs3@andrew.cmu.edu> + * (throughout) Extensive cleanup of how we build static and + shared versions of libsasl. Also some more portability + fixes (Howard Chu <hyc@highlandsun.com>) + +2002-09-04 Rob Siemborski <rjs3@andrew.cmu.edu> + * acconfig.h, configure.in: Actually check for sysexits.h, + varargs.h, and stdarg.h + * lib/checkpw.c: compatibility patch for retry_read + (Howard Chu <hyc@highlandsun.com>) + +2002-09-03 Rob Siemborski <rjs3@andrew.cmu.edu> + * (throughout) fix handling of sys/param.h + * (throughout) fix handling of time.h and sys/time.h + * include/exits.h: include a replacement for sysexits.h + * acconfig.h: define MAXHOSTNAMELEN if it isn't + * lib/getaddrinfo.c, config/ipv6.m4: minor fixes for partial + getaddrinfo/getnameinfo implementations + * (Above changes are all from or based on ideas from + Howard Chu <hyc@highlandsun.com>) + +2002-08-28 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/client.c, lib/saslint.h: Properly handle client-side + serverFQDN and clientFQDN + +2002-08-19 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/dlopen.c: use correct paths when a .la file is not present + (Justin Gibbs <gibbs@scsiguy.com>) + +2002-08-13 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/sysadmin.html: fix some /usr/lib/sasl references to + /usr/lib/sasl2 (Andrew Jones <arjones@simultan.dyndns.org>) + +2002-08-09 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/Makefile.am: fix small parts of the saslauthd.8 build + process. + * Ready for 2.1.7 + +2002-08-06 Ken Murchison <ken@oceana.com> + * plugins/digestmd5.c: disable/remove server-side fast reauth + +2002-08-02 Rob Siemborski <rjs3@andrew.cmu.edu> + * include/sasl.h, lib/common.c: Add SASL_AUTHUSER as a parameter + to sasl_getprop + +2002-08-01 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: allow use of more than one %u or %r in the filter + (Laurent Larquère <llarquere@aacom.fr>) + +2002-07-30 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/client.c, lib/server.c: Add checks for SASL_NEED_PROXY and + SASL_FEAT_ALLOWS_PROXY + * include/sasl.h, include/saslplug.h: Add SASL_NEED_PROXY and + SASL_FEAT_ALLOWS_PROXY + * plugins/digestmd5.c, plugins/gssapi.c, plugins/kerberos4.c, + plugins/otp.c, plugins/plain.c, plugins/srp.c: define + SASL_FEAT_ALLOWS_PROXY for these mechanisms + +2002-07-27 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/auth_sasldb.c: Include mechanisms.h in a reasonable place. + +2002-07-24 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/Makefile.am: Fix DEFS to still supply -I. and -I.. + * configure.in: Make --with-ldap show up in top level configure script, + make saslauthd compile by default + * lib/saslutil.c: use read() and not fread() on /dev/random to preserve + entropy + * doc/sysadmin.html: Add note about using /dev/urandom + +2002-07-19 Rob Siemborski <rjs3@andrew.cmu.edu> + * doc/sysadmin.html, doc/readme.html, doc/upgrading.html: + Misc. documentation cleanup (Joe Rhett <jrhett@isite.net>) + +2002-07-17 Ken Murchison <ken@oceana.com> + * lib/canonusr.c: update length of user string to length of output + from callback + +2002-07-16 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/cram.c: Fix a security problem in the verification of + the digest string. (Andrew Jones <arjones@simultan.dyndns.org>) + * Ready for 2.1.6 + +2002-07-06 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/mysql.c: Further memory management cleanup. (never + strdup the options, and therefore don't free staticly allocated + strings) + * man/sasl_getopt_t.3: Clarify semantics of memory management + +2002-07-05 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/lak.c: Better handling of downed ldap servers + (Igor Brezac <igor@ipass.net>) + * sasldb/db_berkeley.c, utils/dbconverter-2.c: Use db_strerror() + rather than strerror() for Berkeley DB error values. + (J.H.M. Dassen (Ray) <jdassen@debian.org>) + * saslauthd/Makefile.am, saslauthd/auth_ldap.c: don't + hardwire the saslauthd conf file + (J.H.M. Dassen (Ray) <jdassen@debian.org>) + +2002-07-03 Rob Siemborski <rjs3@andrew.cmu.edu> + * man/sasl_user_exists.3: fix sasl_idle reference + +2002-07-02 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/auxprop.c: Can now select multiple auxprop plugins + * doc/options.html: updated for above + * lib/client.c: improve mechanism selection to include + number of security flags + +2002-06-27 Ken Murchison <ken@oceana.com> + * doc/draft-zeilenga-sasl-plain-00.txt: added + * doc/index.html: added PLAIN draft + +2002-06-26 Ken Murchison <ken@oceana.com> + * doc/draft-zeilenga-sasl-anon-00.txt: added + * doc/index.html: added ANONYMOUS draft + +2002-06-20 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/auxprop.c: Make "cound not find auxprop plugin" warning + log at LOG_DEBUG + +2002-06-19 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: create layer keys for integrity as + well as privacy + * saslauthd/auth_ldap.[ch], saslauthd/lak.[ch]: + Large rewrite (Igor Brezac <igor@ipass.net>) + * lib/client.c, lib/server.c, lib/common.c: + Actually set most of the sparams and cparams structures + +2002-06-19 Ken Murchison <ken@oceana.com> + * doc/draft-melnikov-rfc2831bis-01.txt: added + * doc/draft-melnikov-rfc2831bis-00.txt: deleted + * doc/index.html: updated to latest RFC 2831bis draft + +2002-06-18 Ken Murchison <ken@oceana.com> + * doc/draft-nerenberg-sasl-crammd5-02.txt: added + * doc/draft-nerenberg-sasl-crammd5-01.txt: deleted + * doc/index.html: updated to latest CRAM-MD5 draft + +2002-06-17 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/login.c, plugins/plain.c: Canonicalize username before + doing checkpass + +2002-06-14 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/client.c, lib/server.c, lib/saslint.h, lib/common.c. + lib/seterror.c: continued size_t vs unsigned cleanups + +2002-06-13 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/ : remove LDAP support + * Ready for 2.1.5 + +2002-06-12 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/digestmd5.c: rename get_realm to get_server_realm, and + pay attention to its return value + * lib/external.c, lib/seterror.c: cleanup size_t/unsigned confusion + +2002-06-10 Rob Siemborski <rjs3@andrew.cmu.edu> + * sasldb/Makefile.am: fix handling of allockey (only include it once) + * plugins/kerberos4.c: fix a reference count leak + * Ready for 2.1.4 + +2002-05-28 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/LDAP_SASLAUTHD, saslauthd/saslauthd.mdoc: + Update documentation for LDAP and Saslauthd as per + Igor Brezac <igor@ipass.net> + +2002-05-22 Lawrence Greenfield <leg+@andrew.cmu.edu> + * lib/checkpw.c: close door file descriptor in + saslauthd_verify_password + +2002-05-21 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/auth_krb5.c: fix a leak due to not + calling krb5_cc_destroy on failure + +2002-05-17 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/saslauthd-*.c: support a generic mechanism option -O + instead of -H + * saslauthd/auth_ldap.c, lak.c, et. al: auth_ldap overhaul + (Igor Brezac <igor@ipass.net>) + * lib/common.c, include/sasl.h: add sasl_version + +2002-05-13 Rob Siemborski <rjs3@andrew.cmu.edu> + * lib/checkpw.c: use "*cmusaslsecretPLAIN" in auxprop_verify_password + (Howard Chu, <hyc@highlandsun.com>), also only make a single + canon_user call. + +2002-05-13 Ken Murchison <ken@oceana.com> + * plugins/plugin_common.c: set the return code to SASL_FAIL, and + NULL the results of the _plug_get_*() functions before we get + started + * plugins/digestmd5.c, otp.c, plain.c, srp.c: check for NULL or + empty authzid from callback + +2002-05-09 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/configure.in: --with-ldap now takes a path + +2002-05-08 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/acconfig.h, auth_ldap.c, configure.in, lak.c, lak.h: + Misc compile/portability fixes (mostly header-related) + * utils/testsuite.c: minor getopt() parameter fix + (Claus Assmann <ca+sasl@sendmail.org>) + * lib/checkpw.c: fix some warnings + +2002-05-07 Rob Siemborski <rjs3@andrew.cmu.edu> + * Ready for 2.1.3-BETA + +2002-05-06 Rob Siemborski <rjs3@andrew.cmu.edu> + * include/saslplug.h: add name member for canon_user plugins + * lib/canonusr.c: use name member + +2002-05-06 Ken Murchison <ken@oceana.com> + * plugins/digestmd5.c: added client-side reauth + +2002-05-05 Ken Murchison <ken@oceana.com> + * lib/client.c: pass global_context to mech_new() + * lib/server.c: don't free global_context (the plugin should free it) + * utils/testsuite: swapped serverlast tests so that the + descriptions are correct + +2002-05-03 Ken Murchison <ken@oceana.com> + * plugins/digestmd5.c: added server-side reauth + * doc/index.html: added Marshall Rose's SASL papers + * doc/options.html: added 'reauth_timeout' + +2002-05-03 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/kerberos4.c: fix compile errors + * config/kerberos_v4.m4, plugins/digestmd5.c: fix des_cbc_encrypt + interoperability problem (OpenSSL) + * saslauthd/Makefile.am, acconfig.h, auth_ldap.c, auth_ldap.h, + configure.in, lak.c, lak.h, mechanisms.c, mechanisms.h, + saslauthd.conf: added experimental LDAP saslauthd module + (by Igor Brezac <igor@ipass.net>) + * include/saslplug.h: give auxprop plugins a name + * plugins/sasldb.c: give sasldb plugin a name + * lib/auxprop.c: allow auxprop selection + * doc/options.html: document auxprop_plugin option + +2002-05-01 Ken Murchison <ken@oceana.com> + * plugins/digestmd5.c, gssapi.c, kerberos4.c, srp.c: + general plugin cleanup - standardizing structure + +2002-04-30 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c: Minor cleanup of struct hack in context structure + +2002-04-30 Ken Murchison <ken@oceana.com> + * plugins/plugin_common.[ch], anonymous.c, cram.c, login.c, otp.c, + plain.c, sasldb.c, srp.c, + lib/client.c, external.c, saslint.h, server.c: general plugin + cleanup - reusing more common code, standardizing structure + +2002-04-28 Ken Murchison <ken@oceana.com> + * plugins/plugin_common.[ch], anonymous.c, cram.c, digestmd5.c, + gssapi.c, kerberosv4.c, login.c, otp.c, plain.c, srp.c, + lib/external.c:finalize movement of callback/interaction stuff + into plugin_common + +2002-04-27 Ken Murchison <ken@oceana.com> + * plugins/plugin_common.[ch], anonymous.c, cram.c, digestmd5.c, + gssapi.c, kerberosv4.c, login.c, otp.c, plain.c, srp.c, + lib/external.c: move make_prompts stuff into plugin_common + * utils/testsuite.c: allow for testing of EXTERNAL + +2002-04-26 Rob Siemborski <rjs3@andrew.cmu.edu> + * sasldb/allockey.c: be sure to set userPassword and not *userPassword + +2002-04-26 Ken Murchison <ken@oceana.com> + * lib/client.c, server.c: check 'doneflag' just before mech_step() + * plugins/plugin_common.[ch], anonymous.c, cram.c, digestmd5.c, + gssapi.c, kerberosv4.c, login.c, otp.c, plain.c, srp.c, + lib/external.c, Makefile.am: move callback/interaction stuff + into plugin_common + * plugins/plugin_common.[ch], digestmd5.c, gssapi.c, + kerberosv4.c, srp.c: move decode/concatenation of multiple + packets into plugin_common + * utils/testsuite.c: set SASL_AUTH_EXTERNAL so we can test EXTERNAL + +2002-04-25 Ken Murchison <ken@oceana.com> + * plugins/otp.c: don't free the secret when we get data from a + callback (and don't copy it) + * plugins/gssapi.c, plain.c: make sure to set 'doneflag' when done + * lib/client.c, server.c: don't call mech_step() if 'doneflag' is set + +2002-04-24 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/cram.c, digestmd5.c, login.c, plain.c, srp.c: don't + free the secret when we get data from a callback (and don't copy it) + +2002-04-22 Rob Siemborski <rjs3@andrew.cmu.edu> + * include/gai.h: Fix for compatibility with older glibc versions + (Howard Chu, <hyc@highlandsun.com>) + * plugins/gssapi.c: Don't always send authzid on client side + (Howard Chu, <hyc@highlandsun.com>) + +2002-04-18 Rob Siemborski <rjs3@andrew.cmu.edu> + * saslauthd/auth_sasldb.c: Use "use_realm" instead of "realm" + for lookup of secret. (Jonas Oberg <jonas@gnu.org>) + * plugins/gssapi.c: Correct handling of client-side authid and + authzid (Howard Chu, <hyc@highlandsun.com>) + * lib/external.c: Better handling of user canonicalization + (Howard Chu, <hyc@highlandsun.com>) + * plugins/cram.c, digestmd5.c, gssapi.c, kerberos4.c, + login.c, otp.c, plain.c, srp.c: zero out prompt_need structures + before use + +2002-04-17 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/cram.c, digestmd5.c, srp.c: Adjust cmusaslsecretFOO to + *cmusaslsecretFOO + * plugins/sasldb.c: correctly handle *(property) + * lib/canonusr.c, server.c: Lookup authzid and authid auxprops + correctly (and in the same place). + * include/sasl.h, saslplug.h: Fix auxprop lookups + (e.g. SASL_AUXPROP_AUTHZID) + +2002-04-15 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c: Handle null authzid's correctly + * lib/server.c: fix a strcmp() that should be a memcmp() + +2002-04-15 Rob Siemborski <rjs3@andrew.cmu.edu> + * plugins/gssapi.c: fix how name_token and name_without_realm are + freed. + +2002-04-12 Ken Murchison <ken@oceana.com> + * doc/draft-melnikov-rfc2831bis-00.txt: added + * doc/draft-myers-saslrev-02.txt: moved TOC + * doc/draft-myers-saslrev-02.txt: added + * doc/draft-myers-saslrev-01.txt: deleted + * doc/index.html: changed link to updated saslrev draft, + added KERBEROS_V4 notation, + added link to rfc2831bis draft + +2002-04-08 Ken Murchison <ken@oceana.com> + * lib/server.c, doc/options.html: allow multiple pwcheck_methods + +2002-04-03 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/configure.in: properly define AUTH_KRB5 + * saslauthd/auth_krb5.c: changes for MIT KRB5 + +2002-03-27 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Removed check for db3/db.h (people can just use --with-bdb-incdir) + +2002-03-26 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Ready for 2.1.2 + +2002-03-11 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/kerberos4.c: Fix a race condition during mutex allocation + +2002-03-04 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/checkpw.c: Stop logging "authentication failed" message + * plugins/gssapi.c: Reduce log level of "gss_accept_context" message + +2002-02-27 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/saslauthd.mdoc: Clarify that sasldb with saslauthd + is not what you want to be doing. + * doc/sysadmin.html: Update "sasldb" verifier to "auxprop" + +2002-02-22 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/checkpw.c: made retry_read static + +2002-02-21 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/checkpw.c (auxprop_verify_password) report SASL_NOUSER instead + of SASL_FAIL. + * lib/client.c, lib/server.c: More Complete returning of SASL_NOTINIT + * utils/testsuite.c: Better checking for SASL_NOTINIT + +2002-02-11 Ken Murchison <ken@oceana.com> + * plugins/srp.c: removed OpenSSL 0.9.6 dependencies, small bugfix + * configure.in: cleaned up OpenSSL (libcrypto) check + +2002-02-05 Rob Siemborski <rjs3+@andrew.cmu.edu> + * contrib/tclsasl: Add Marshall Rose's <mrose@dbc.mtview.ca.us> + tclsasl patch. + * plugins/anonymous.c: No longer append extra NUL to client response + +2002-02-04 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/saslpasswd.c: Added -n option (Ken Murchison) + * lib/dlopen.c: Removed confusing entry point message. + * Ready for 2.1.1 + +2002-02-01 Ken Murchison <ken@oceana.com> + * plugins/srp.c: fixed srp_setpass() + +2002-01-31 Ken Murchison <ken@oceana.com> + * include/sasl.h, lib/server.c, + plugins/digestmd5.c, gssapi.c, kerberos4.c, srp.c: + added SASL_SEC_MUTUAL_AUTH + * plugins/srp.c: cleanup error messages and return codes + +2002-01-30 Ken Murchison <ken@oceana.com> + * plugins/otp.c, plugins/otp.h: added non-OPIE client/server + implementation (requires OpenSSL) + * configure.in: OTP now requires OpenSSL, OPIE is optional + * doc/options.html, doc/readme.html, doc/sysadmin.html, doc/TODO: + updated for new OTP implementation + +2002-01-25 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/Makefile.am: Correct multiple EXTRA_DIST bug + * saslauthd/Makefile.am: small typo fixed (Leena Heino <liinu@uta.fi>) + +2002-01-23 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/dbconverter-2.c (main): More intelligent default paths + * acconfig.h: #ifndef's for _GNU_SOURCE (Assar <assar@permabit.com>) + +2002-01-22 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/common.c: Complete definition of sasl_global_listmech + (from Love <lha@stacken.kth.se>) + * lib/client.c: added checks for _sasl_client_active to + sasl_client_new and sasl_client_start + +2002-01-21 Ken Murchison <ken@oceana.com> + * doc/draft-myers-saslrev-01.txt: moved TOC + * doc/draft-ietf-cat-sasl-gssapi-05.txt: moved TOC + * doc/draft-nerenberg-sasl-crammd5-01.txt: added + * doc/draft-nerenberg-sasl-crammd5-00.txt: deleted + * doc/index.html: changed link to updated draft + * plugins/login.c (login_client_mech_step): fix client-first + handling + +2002-01-21 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/server.c (sasl_server_start): null out *serverout and + *serveroutlen, just in case. + * lib/external.c: Added correct required_prompts + * saslauthd/testsaslauthd.c: Added simple saslauthd client + * saslauthd/Makefile.am: rules for testsaslauthd + * doc/sysadmin.html: updated to reference testsaslauthd + * saslauthd/saslauthd.c: allow -n 0 (for fork-per-connection) + * saslauthd/saslauthd.mdoc: documentation of -n 0 + * plugins/cram.c (crammd5_client_mech_step): fix client-first + handling + * sasldb/db_gdbm.c: improved error reporting + (Courtesy Marshall T. Rose <mrose@dbc.mtview.ca.us> + * config/sasldb.m4: improved gdbm configure handling + (Courtesy Marshall T. Rose <mrose@dbc.mtview.ca.us> + * config/kerberos_v4.m4: Detect OpenSSL libdes first. + (Courtesy Marshall T. Rose <mrose@dbc.mtview.ca.us> + * plugins/cram.c, digestmd5.c, kervberos4.c, login.c, + lib/client.c, server.c, include/saslplug.h: + Cleaner client-first ABI. + +2002-01-19 Ken Murchison <ken@oceana.com> + * plugins/otp.c: set serverout to NULL where we have nothing to + send instead of the empty string + * plugins/srp.c: let glue code handle client-last/server-last + situation by setting serverout appropriately + +2002-01-19 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/plain.c, plugins/login.c, plugins/digestmd5.c: + set serverout to NULL where we have nothing to send instead of + the empty string + * include/saslplug.h, lib/client.c, lib/server.c: eliminated + SASL_FEAT_WANT_SERVER_LAST in favor of clever setting of serverout + * plugins/digestmd5.c: removed SASL_FEAT_WANT_SERVER_LAST + +2002-01-18 Ken Murchison <ken@oceana.com> + * plugins/srp.c: updated to draft-burdis-cat-srp-sasl-06 + * plugins/srp.c: server uses external SSF + * plugins/srp.c: server sends mandatory options based on min SSF + * doc/draft-burdis-cat-srp-sasl-06.txt: added + * doc/draft-burdis-cat-srp-sasl-05.txt: deleted + * doc/index.html: changed link to updated draft + +2002-01-17 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/kerberos4.c: Actually allocate a mutex on the client side + +2002-01-16 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/server.c (mech_permitted): fixed incorrect return value of + SASL_NOMECH that should have been 0. + * lib/common.c (sasl_errdetail): fixed core if passed in conn is NULL + * plugins/digestmd5.c (encode_tmp_buf): removed unneeded buffer + +2002-01-16 Ken Murchison <ken@oceana.com> + * plugins/srp.c: fixed layer decoding to handle multiple packets + * plugins/srp.c: plugged memory leaks (now passes testsuite) + * plugins/srp.c: more logging + * plugins/srp.c: lots of other nits, bug fixes + * utils/testsuite.c: added SSF=0/56 test + +2002-01-14 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/auth_krb4.c (auth_krb4): fix tf_name memory leak, + and other efficency fixes + +2002-01-11 Rob Siemborski <rjs3+@andrew.cmu.edu> + * include/saslplug.h: Add flags member to params structures + * lib/client.c, lib/server.c: flags parameter to sasl_*_new + now gets to the plugins + +2002-01-10 Rob Siemborski <rjs3+@andrew.cmu.edu> + * include/sasl.h: Update for sasl_global_listmech API + * lib/common.c, lib/client.c, lib/server.c: sasl_global_listmech() + * lib/dlopen.c (_parse_la): fix parseing of dlname= line + * Ready for 2.1.0 + +2002-01-09 Ken Murchison <ken@oceana.com> + * plugins/otp.c: fixed security_flags + * plugins/srp.c: corrected integrity layer encoding + * plugins/srp.c: finished maxbuffersize handling + * plugins/srp.c: fixed security_flags + * doc/index.html: added reference to SRP paper + +2002-01-09 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/common.c (sasl_decode): Removed maxoutbuf check + * man/sasl_setprop.3: Minor clarifications + * plugins/digestmd5.c, plugins/gssapi.c, plugins/kerberos4.c: + Assorted security layer fixes (maxoutbuf setting, mech_ssf setting) + * lib/common.c, lib/client.c, lib/server.c, lib/saslint.h: + Allowed client-side sasl_listmech calls. + * include/sasl.h: Minor cosmetic fix to comments + * doc/programming.html: Interaction memory management clarifications + * lib/common.c: Fix several crash problems in getprop + (Courtesy Marshall T. Rose <mrose@dbc.mtview.ca.us>) + +2002-01-05 Lawrence Greenfield <leg+@andrew.cmu.edu> + * saslauthd/saslauthd.c: F_SETLK doesn't block; F_SETLKW does + * saslauthd/saslauthd.c: detect errors somewhat better + +2002-01-04 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/common.c: Allow sasl_setprop for SASL_DEFUSERREALM + +2002-01-04 Ken Murchison <ken@oceana.com> + * plugins/srp.c: don't send M2 if using a confidentiality layer + * plugins/srp.c: more constraint checks + * plugins/otp.c: improve standard hex/word response detection + * doc/install.html, doc/sysadmin.html, contrib/opie-2.4-fixes: + add patch for OPIE 2.4 to enable extended responses + +2002-01-03 Ken Murchison <ken@oceana.com> + * configure.in: removed check fpr gmp + * plugins/srp.c: migrated to OpenSSL's BN (removed GNU MP dependency) + +2001-12-20 Rob Siemborski <rjs3+@andrew.cmu.edu> + * sasldb/db_ndbm.c: Fixed small memory leak + (Courtesy Howard Chu <hyc@highlandsun.com>) + +2001-12-18 Ken Murchison <ken@oceana.com> + * plugins/srp.c: more constraint checks + +2001-12-17 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/saslauthd.c: Prefork a number of processes to handle + connections. + * saslauthd/auth_krb4.c: Handle concurrent accesses better. + +2001-12-15 Ken Murchison <ken@oceana.com> + * plugins/srp.c: added confidentiality layers + +2001-12-14 Ken Murchison <ken@oceana.com> + * plugins/srp.c: improved client/server layer option handling + * plugins/srp.c: added client-side support for mandatory options + * plugins/srp.c: added framework for confidentiality layers + * plugins/srp.c: added some data sanity checking (thanks to + Tom Holroyd <tomh@po.crl.go.jp> for feedback) + +2001-12-13 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/server.c, lib/common.c: Fix handling of + global callbacks so that plugin_list works again + +2001-12-12 Rob Siemborski <rjs3+@andrew.cmu.edu> + * pwcheck/Makefile.am: Added include of ../lib + (from Hajimu UMEMOTO <ume@mahoroba.org>) + +2001-12-11 Rob Siemborski <rjs3+@andrew.cmu.edu> + * sasldb/db_ndbm.c: fix call to dbm_nextkey, from + Scot W. Hetzel <scot@genroco.com> + +2001-12-10 Rob Siemborski <rjs3+@andrew.cmu.edu> + * doc/plugprog.html: Update for new user canonicalization usage. + * man/sasl_canon_user.3: Update for new user canonicalization usage. + * configure.in: Actually set STATIC_GSSAPIV2 when necessary + +2001-12-08 Ken Murchison <ken@oceana.com> + * plugins/srp.c: make sure we have the HMAC before trying to use it + * plugins/srp.c: don't advertise server integrity w/o HMAC-SHA-1 + * plugins/srp.c: move EVP_cleanup() to mech_free so mech can be reused + +2001-12-07 Ken Murchison <ken@oceana.com> + * configure.in: SRP now requires OpenSSL + * plugins/srp.c: migrated to OpenSSL's MDA/cipher abstraction API + * plugins/srp.c: added RIPEMD-160 support + * plugins/srp.c: using "standard ACSII names" for MDA-names as + documented by [SCAN] (until determined otherwise) + * plugins/srp.c: using updated canon_user API to allow separate + canonicalization of authid and authzid. + +2001-12-06 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/canonusr.c: Better logging when desired plugin is not found. + * lib/checkpw.c: spelling error fixed. + * lib/canonusr.c, lib/checkpw.c, lib/client.c, lib/external.c, + lib/saslint.h, lib/server.c, include/sasl.h, include/saslplug.h, + plugins/*.c: Updated canon_user API to allow separate + canonicalization of authid and authzid. + +2001-12-05 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/Makefile.am, saslauthd/acconfig.h, saslauthd/configure.in: + Solaris 7 and FreeBSD (FreeBSD is courtesy of Claus Assmann + <ca+sasl@sendmail.org>) + * sasldb/Makefile.am: link order fix (Courtesy Claus Assmann + <ca+sasl@sendmail.org>) + +2001-12-05 Ken Murchison <ken@oceana.com> + * configure.in: + * plugins/Makefile.am: only build SRP with sasldb libs when + srp_setpass() is enabled + * plugins/srp.c: added HMAC-SHA-160 integrity layer + * plugins/srp.c: don't offer integrity layers unless HMAC-SHA-160 + is available (mandatory) + * plugins/srp.c: fixed multiple integrity/confidentiality layer + client-side bug + * plugins/srp.c: fixed delete SRP secret bug + * plugins/srp.c: removed VL() stuff + +2001-12-04 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/Makefile.am, config/sasldb.m4: Build sasldblistusers2 + and saslpasswd2. Default database now /etc/sasldb2 + * INSTALL, README, doc/index.html, doc/upgrading.html: Update + with upgrading instructions in preparation for release. + * doc/, /: Documentation reorganization, convert README and INSTALL to + HTML format. + * Bumped appropriate version numbers, Ready for 2.0.5-BETA + +2001-12-04 Ken Murchison <ken@oceana.com> + * acconfig.h, configure.in: dependency checking for SRP + * acconfig.h, configure.in: + * plugins/srp.c: made srp_setpass() a compile-time option (default=off) + * plugins/srp.c: use auxprop to fetch cmusaslsecretSRP/userPassword + * plugins/srp.c: code cleanup + * acconfig.h, configure.in: + * doc/sysadmin.html: + * plugins/otp.c: made otp_setpass() a compile-time option (default=off) + +2001-12-02 Ken Murchison <ken@oceana.com> + * plugins/srp.c: fixed SHA1 support + * plugins/srp.c: changed calculation of 'x' to coincide with draft -05 + * plugins/srp.c: code cleanup + +2001-12-01 Ken Murchison <ken@oceana.com> + * plugins/srp.c: abstracted MDA interface + * plugins/srp.c: added SHA1 support (not working) + +2001-11-30 Ken Murchison <ken@oceana.com> + * plugins/srp.c: renumbered steps to start at 1 + * plugins/srp.c: check plugin API version instead of SRP_VERSION + * plugins/srp.c: changed data exchanges to conform to draft -05 + +2001-11-29 Ken Murchison <ken@oceana.com> + * plugins/srp.c: code now compiles and runs + * plugins/Makefile.am: added sasldb libs to SRP build + +2001-11-24 Ken Murchison <ken@oceana.com> + * lib/external.c: made EXTERNAL a client-send-first mechanism + * doc/index.html: added CRAM-MD5 draft + +2001-11-22 Ken Murchison <ken@oceana.com> + * plugins/otp.c: fixed otp_setpass() bug + * doc/sysadmin.html: OTP additions/changes + +2001-11-19 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/saslpasswd.c: Corrected disable handling + +2001-11-17 Ken Murchison <ken@oceana.com> + * doc/index.html, rfc2945.txt, rfc3174.txt: specification additions + * doc/Makefile.am: Updated included RFCs and IDs + +2001-11-14 Ken Murchison <ken@oceana.com> + * lib/server.c, doc/options.html: added 'mech_list' option + +2001-11-14 Rob Siemborski <rjs3+@andrew.cmu.edu> + * sasldb/allockey.c: removed an assert() call + * sasldb/db_ndmb.c, sasldb/db_gdbm.c: Fixed cntxt's to be conn's + +2001-11-13 Ken Murchison <ken@oceana.com> + * acconfig.h, configure.in: + * plugins/otp.c: support client-side OTP without OPIE + +2001-11-08 Ken Murchison <ken@oceana.com> + * plugins/otp.c: allow entry of one-time password via + SASL_CB_ECHOPROMPT callback + * plugins/otp.c: code cleanup + * doc/index.html, draft*.txt: specification updates/additions + +2001-11-08 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/cram.c, digestmd5.c, sasldb.c: Removed all assert() + calls from supported plugins. + +2001-11-07 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/testsuite.c: added proxy policy checks + * lib/checkpw.c (_sasl_auxprop_verify_apop): correct handling + of seterror calls + +2001-11-06 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/canonusr.c (_canonuser_internal): added necessary seterror calls + * doc/Makefile.am: Updated included RFCs and IDs + * lib/canonusr.c, lib/server.c: Corrected authzid/authid handling + * plugins/digestmd5.c: Unconfused authzid/authid in server call to + canon_user + +2001-11-01 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/gssapi.c, plugins/kerberos4.c: Get rid of unnecessary + buffer copy in security layer encodes. + +2001-10-24 Ken Murchison <ken@oceana.com> + * plugins/otp.c: added otp_setpass() so that saslpasswd can + be used instead of opiepasswd on closed systems + * doc/sysadmin.html: OTP additions/changes + +2001-10-22 Ken Murchison <ken@oceana.com> + * acconfig.h, configure.in: detect OPIE, enable/disable OTP + * plugins/Makefile.am, makeinit.sh, otp.c: added OTP support + (still need work on RFC2444 compliance - depends on OPIE changes) + * doc/index.html, options.html, sysadmin.html, rfc*.txt: + OTP additions/changes + +2001-10-18 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/testsuite.c: Test DES harder for DIGEST-MD5 + * plugins/digestmd5.c (enc_des): Get rid of one buffer copy. + * plugins/digestmd5.c (dec_des, dec_3des): correct handling of + padding length check. + +2001-10-17 Rob Siemborski <rjs3+@andrew.cmu.edu> + * config/sasldb.m4: detect berkeley db 4 + * plugins/gssapi.c, cram.c, kerberos4.c, digestmd5.c: have dispose + calls deal with the possibility of a null context + +2001-10-16 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/Makefile.am: Link LIB_PAM as well, if needed + * plugins/digestmd5.c: Don't send a trailing nul on challenge and + responses. + * lib/server.c (sasl_server_start, sasl_server_step): Deal with + authentication failures better. (Reported by Larry Rosenbaum + <lmr@ornl.gov>) + +2001-10-02 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/Makefile.am, saslauthd/auth_sasldb.c, + saslauthd/configure.in: Changes to allow extraction of saslauthd + as needed. + +2001-09-19 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/getaddrinfo.c (getaddrinfo): Correct fix for + AI_PASSIVE bug from Hajimu UMEMOTO <ume@mahoroba.org> + * plugins/plugin_common.c, lib/common.c (_*_ipfromstring): + revert to previous versions. + + * plugins/Makefile.am: Include necessry compatibility objects + as needed. + * lib/Makefile.am: compatibility code for static libsasl + * configure.in: small changes to make compatibility objects easy + to use. + +2001-09-18 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/plugin_common.c, lib/common.c (_*_ipfromstring): + no longer use AI_PASSIVE hint for getaddrinfo + +2001-09-13 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/auth_sasldb.c, saslauthd/auth_sasldb.h: + Added experimental sasldb saslauthd module + * saslauthd/configure.in: sasldb related config changes, + do not config if disabled + +2001-09-12 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/*, lib/checkpw.c (saslauthd_verify_password): + merged new saslauthd protocol from Ken Murchison <ken@oceana.com> + +2001-08-30 Rob Siemborski <rjs3+@andrew.cmu.edu> + + * configure.in, saslauthd/configure.in: check for inet_aton + in libresolv.so, so as to link it if necessary + + * config/sasldb.m4 (BERKELEY_DB_CHK_LIB): set runpath of library + if necessary + +2001-08-29 Rob Siemborski <rjs3+@andrew.cmu.edu> + + * utils/testsuite.c: Minor testsuite fix (include paths) + + * Ready for 2.0.4-BETA + +2001-08-24 Rolf Braun <rbraun+@andrew.cmu.edu> + + * Mac OS 9 and X support, including Carbon + Mac OS 9 Classic support based on the SASL v1 code + by Aaron Wohl <n3liw+@andrew.cmu.edu> + + * updated ltconfig and ltmain.sh + * acconfig.h: + * configure.in: + * lib/saslutil.c: use random() when jrand48() isn't available + + * dlcompat-20010505: + dlcompat included for OS X support, compiles separately + * lib/dlopen.c: prefix symbols with underscore on OS X, as on OpenBSD + note that this is also detected automatically by configure, + this only helps when cross-compiling (for OS X?) + + * acconfig.h: + * configure.in: + * config/kerberos_v4.m4 + look for libdes524 when libdes doesn't exist. + look for libkrb4 when libkrb doesn't exist. + + * lib/saslint.h: + * lib/common.c: + * lib/seterror.c: + * lib/Makefile.am: + split sasl_seterror() into a new file. + add_string -> _sasl_add_string and made this non-static + so seterror can use it. + added _sasl_get_errorbuf to go into the conn_t struct + so we don't have to know the format of that struct when + seterror.c is linked from glue code (i.e., the Mac OS X CFM glue) + + * acconfig.h: + fix the order of the fake iovec struct for systems that + don't have it (like Mac OS 9) so it's the same order as + most Unixes that do (like Mac OS X) -- the CFM glue needs this + + * acconfig.h: + include <sys/types.h> before we include <sys/uio.h> + + * plugins/kerberos4.c: + * lib/checkpw.c: + * acconfig.h: + * configure.in: + check for krb_get_err_txt in the kerberos 4 library, + and use it instead of the krb_err_txt[] array if available + + * plugins/kerberos4.c: + define KEYFILE to "/etc/srvtab" if not already defined + by the kerberos 4 headers (needed for MIT KfM 4.0) + + * doc/macosx.html: added this + * README: point Mac OS X users to doc/macosx.html + * doc/Makefile.am: add doc/macosx.html to distfiles + + * Makefile.am: + * lib/Makefile.am: + * include/Makefile.am: + * config/Info.plist: + * configure.in: + when building on Mac OS X, install a framework + in /Library/Frameworks + + * mac/*: + projects and support files for Mac OS 9, classic and Carbon + * mac/osx_cfm_glue: + the glue to allow CFM Carbon applications under Mac OS X + call the Unix-layer SASL library + + * lib/common.c: + * lib/canonusr.c: + don't do the auxprop stuff on Mac OS 9 + + * lib/getaddrinfo.c: + don't look up hostnames on Mac OS 9 (we only officially + support passing IP address strings anyway) + + * lib/getaddrinfo.c: + * plugins/plugin_common.c: + * plugins/plugin_common.h: + don't include headers on Mac OS 9 that we don't have. + + * sample/sample-client.c: + add a cast for Mac OS 9 (different type handling of char) + + * plugins/makeinit.sh: + include the stub header to export the right symbols on Mac OS 9 + +2001-08-20 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/gssapi.c (gssapi_server_mech_step): fixed accidental + back link into glue code + + * config/kerberos4.m4: Actually link in -lkrb + +2001-08-15 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/common.c (_sasl_iptostring): #if 0'd out. + + * lib/server.c (sasl_user_exists): only check the verifier we + are using + + * config/kerberos_v4.m4 (SASL_DES_CHK): added + * config/kerberos_v4.m4 (SASL_KERBEROS_V4_CHK): included + entire check from configure.in + * configure.in: moved kerberos 4 code completely out. + * saslauthd/acconfig.h (WITH_DES, WITH_SSL_DES): Added + DES-related symbols + +2001-08-14 Rob Siemborski <rjs3+@andrew.cmu.edu> + * configure.in: Check for sys/uio.h + * saslauthd/configure.in: Check for sys/uio.h + * config.h: Do the Right Thing for struct iovec (and + no longer include sys/uio.h elsewhere) + * saslauthd/config.h: Do the Right Thing for struct iovec (and + no longer include sys/uio.h elsewhere) + +2001-08-13 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/digestmd5.c (init_des, init_3des, enc_des, dec_des, + enc_3des, dec_3des): fixed interoperability problems, + 3des was not decrypting with correct key and des was not + setting up the initial vector. + + * lib/checkpw.c (always_true): log users who log in via this verifier + +2001-08-13 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/testsuite.c (giveokpath): fix memory leak + + * lib/common.c (sasl_ipfromstring): add call to freeaddrinfo() + * plugins/plugin_common.c (_plug_ipfromstring): add call to + freeaddrinfo() + + * lib/saslutil.c (sasl_randseed): actually initialize the randpool + + * saslauthd/auth_getpwent.c (auth_getpwent): clear a warning + * saslauthd/auth_shadow.c (auth_shadow): clear a similar warning + + * utils/Makefile.am (EXTRA_DIST): Actually include the needed files + + * saslauthd/configure.in: Handle shadow passwords correctly + * saslauthd/acconfig.h: Handle shadow passwords correctly + + * lib/checkpw.c (always_true): added + * configure.in: added check for alwaystrue verifier + * acconfig.h: added HAVE_ALWAYSTRUE + * doc/options.html: alwaystrue verifier documented + +2001-08-11 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/: Now configures separately from SASL, so as + to localize tests for that package within that package + + * utils/dbconverter-2.c (listusers_cb): fix handling of APOP + +2001-08-10 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/Makefile.am (install-data-local): + correct handling of $(DESTDIR) (and create the directory if it + isn't there) [Amos Gouaux <amos@utdallas.edu>] + + * lib/server.c (sasl_server_init): Added plugname to add_plugin + call for EXTERNAL + + * doc/index.html: updated + * doc/appconvert.html: cleaned up + +2001-08-09 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/digestmd5.c (digestmd5_client_mech_step): handle + missing authorization name + * plugins/plain.c (plain_client_mech_step): handle + missing authorization name + + * include/sasl.h: better documentation of SASL_CB_CANON_USER + +2001-08-08 Rob Siemborski <rjs3+@andrew.cmu.edu> + * saslauthd/saslauthd.mdoc: updated re: pam + * saslauthd/saslauthd.8: regenerated + * saslauthd/Makefile.am: Link against PLAIN_LIBS also + (from Ken Murchison <ken@oceana.com>) + +2001-08-07 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/client.c (sasl_server_step): corrected maxoutbuf handleing + * lib/server.c (sasl_server_step): corrected maxoutbuf handleing + * lib/saslint.h (DEFAULT_MAXOUTBUF): removed + + * lib/common.c (sasl_encodev, sasl_decode): maxbufsize checking + + * utils/testsuite.c (testseclayer,doauth): more security layer + checking. Added parameter to doauth to disable fatal() calls, + updated all callers. + + * utils/smtptest.c (main): added ability to support LMTP + + * plugins/gssapi.c: conform with draft-ietf-cat-sasl-gssapi-05.txt + + * doc/draft-ietf-cat-sasl-gssapi-05.txt: added + * doc/Makefile.am (EXTRA_DIST): added above to EXTRA_DIST + +2001-08-06 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/dbconverter-2.c (listusers_cb): handle PLAIN-APOP + + * lib/client.c (sasl_client_add_plugin, client_done): + save plugin name + * lib/server.c (sasl_server_add_plugin, server_done): + save plugin name + * lib/dlopen.c (_sasl_plugin_load): correctly pass pluginname + * lib/common.c (sasl_getprop): implement SASL_AUTHSOURCE properly + * lib/saslint.h (cmechanism_t, mechanism_t): added plugname field + * lib/canonusr.c (internal_canonuser_init): no longer limit + based on plugname + * plugins/sasldb.c (sasldb_auxprop_plug_init): no longer limit + based on plugname + +2001-08-01 Rob Siemborski <rjs3+@andrew.cmu.edu> + * utils/smtptest.c (iptostring): better behaved w.r.t endianness + + * plugins/cram.c (crammd5_server_mech_step): support for old-style + secrets + * plugins/digestmd5.c (digestmd5_server_mech_step): support for + old-style secrets + * lib/checkpw.c (auxprop_verify_password,_sasl_make_plain_secret): + support for old-style secrets + * utils/dbconverter-2.c: added + * utils/sasldblistusers.c (listusers): Print out property names + as well as username@realm format. + * utils/saslpasswd.c (_sasl_sasldb_set_pass): Correctly handle updates + that concern old-style secrets + + * sasldb/allockey.c: Added a missing null to propName in key parser + +2001-07-31 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/kerberos4.c (mech_avail): made static + + * plugins/kerberos4.c (mech_avail): fixed ipv4 check + (patch from Hajimu UMEMOTO <ume@mahoroba.org>) + + * doc/appconvert.html: vague guide documenting our experience + porting Cyrus IMAPd to use SASLv2 + * doc/Makefile.am: added appconvert.html + + * lib/client.c (sasl_client_new): fixed ip address setting to hit + relevant params structures as well + * lib/server.c (sasl_server_new): fixed ip address setting to hit + relevant params structures as well + * lib/common.c (sasl_setprop): fixed ip address setting to hit + relevant params structures as well + + * lib/common.c (sasl_seterror): fixed spelling error + +2001-07-30 Rob Siemborski <rjs3+@andrew.cmu.edu> + * sasldb/db_berkeley.c: utils->seterror() calls + * sasldb/db_gdbm.c: utils->seterror() calls + * sasldb/db_ndbm.c: utils->seterror() calls + * sasldb/allockey.c: utils->seterror() calls + + * lib/common.c (sasl_seterror): still call logging callback with a + null sasl_conn_t + + * plugins/sasldb.c (sasldb_auxprop_lookup): support for multiple + properties + + * plugins/Makefile.am: added -module to LDFLAGS + + * config/sasldb.m4: Allow specification of exact berkeley db + lib and include paths + * sasldb/Makefile.am: Add proper include directory + + * sasldb/sasldb.m4 (SASL_DB_BACKEND_STATIC): include allockey.o + + * Ready for 2.0.3-BETA + + * plugins/kerberos4.c (kerberos4_server_plug_init): reset + srvtab when we do not load correctly. + + * lib/staticopen.c (_sasl_load_plugins): do not fail + if a single plugin load fails + + * include/sasl.h (SASL_CLIENT_FALLBACK): removed + +2001-07-27 Rob Siemborski <rjs3+@andrew.cmu.edu> + * configure.in: extracted SASLDB-related checking + * config/sasldb.m4: added + + * configure.in: now cache the JNI include directory path + + * utils/testsuite.c: switch some sasl_errstrings to sasl_errdetail + * plugins/gssapi.c: Fix error reporting + + * plugins/gssapi.c: Required SASL_CB_USER instead of SASL_CB_AUTHNAME + + * plugins/anonymous.c: Function name standardization + * plugins/cram.c: Function name standardization + * plugins/digestmd5.c: Function name standardization + * plugins/gssapi.c: Function name standardization + * plugins/kerberos.c: Function name standardization + * plugins/login.c: Function name standardization + * plugins/plain.c: Function name standardization + + * sasldb/allockey.c: Generalized SASLdb API + * sasldb/db_berkeley.c: Generalized SASLdb API + * sasldb/db_gdbm.c: Generalized SASLdb API + * sasldb/db_ndbm.c: Generalized SASLdb API + * sasldb/db_none.c: Generalized SASLdb API + * sasldb/db_testw32.c: Added #error to block compile so the API will + be fixed when we do the Win 32 port + * plugins/sasldb.c: Use new SASLdb API + * utils/saslpasswd.c: Use new SASLdb API + +2001-07-26 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/common.c (_sasl_getcallback): fixed reference to + possibly NULL conn + + * configure.in: only build saslpasswd and sasldblistusers + if we have a meaningfull libsasldb (e.g. not db_none), + * utils/Makefile.am: only build saslpasswd and sasldblistusers + if we have a meaningfull libsasldb (e.g. not db_none), + + * configure.in: conditionally build smtptest + * utils/Makefile.am: conditionally build smtptest + + * sasldb/allockey.c (_sasldb_parse_key): added + + * sasldb/sasldb.h: New key list access API, added parameter to + sasl_check_db (all callers updated, all callees updated) + * sasldb/db_berkeley.c: Implement key list access API + * sasldb/db_gdbm.c: Implement key list access API + * sasldb/db_ndbm.c: Implement key list access API + * sasldb/db_none.c: Implement key list access API + + * utils/sasldblistuser.c: Use libsasldb instead of internal + functions. + + * utils/saslpasswd.c: No longer have separate global_utils, + call sasl_dispose and sasl_done + + * acconfig.h: check for inttypes.h + * configure.in: check for inttypes.h + * plugins/plugin_common.c: include, if necessary, inttypes.h, + reference uint32_t instead of u_int32_t + +2001-07-25 Rob Siemborski <rjs3+@andrew.cmu.edu> + * lib/saslint.h: changed "sasldb" verifier to "auxprop" + * lib/server.c: changed "sasldb" verifier to "auxprop" + * lib/checkpw.c: changed "sasldb" verifier to "auxprop" + * utils/testsuite.c: changed "sasldb" verifier to "auxprop" + * doc/options.html: changed "sasldb" verifier to "auxprop" + + * README: updated upgrade information + + * utils/Makefile.am (CLEANFILES): added + + * sasldb/allockey.c (alloc_key): single place for alloc_key() + Removed alloc_key from other source files. + * sasldb/sasldb.h: added declaration of alloc_key() + + * configure.in: added checks for db-3.3 and db3.3 + + * plugins/digestmd5.c (get_realm): now error on empty user_realm + + * plugins/cram.c (client_required_prompts): removed redundant + required_prompts + + * plugins/plain.c (client_continue_step): server-send-last error + + * utils/testsuite.c (main): detailed client-send-first, + server-send-last checking + +2001-07-24 Rob Siemborski <rjs3+@andrew.cmu.edu> + * plugins/sasldb.c: Cleaned up calls into the glue code + + * java/Test/*: Cleaned up java test utilities + + * configure.in: Minor GSSAPI configure changes + + * utils/saslpasswd.c: Clarfied -d option for saslpasswd + * utils/saslpasswd.8: Clarfied -d option for saslpasswd + + * doc/plugprog.html: Added plugin programmer's guide + * doc/index.html: linked to plugin programmer's guide + + * configure.in: corrected configure checking of Berkeley DB + (from Scot W. Hetzel <scot@genroco.com>) + + * configure.in: corrected checking for libcom_err + (from Scot W. Hetzel <scot@genroco.com>) + +2001-07-23 Rob Siemborski <rjs3+@andrew.cmu.edu> + * configure.in: Added check for db3/db.h + + * plugins/kerberos4.c Added mech_avail (checks for IP info) + + * lib/common.c: Fixed setting of serverFQDN in _sasl_conn_init + + * lib/server.c: Fully Implemented mech_avail calls in glue code + + * lib/server.c: Fixed allocation/destruction of sasl_conn_t's + * lib/client.c: Fixed allocation/destruction of sasl_conn_t's + * lib/common.c: Rely on earlier initialization in server.c and client.c + + * doc/options.html: added + + * ChangeLog: back to standard format + +2001-07-20 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Can now deal with variable client-first mechs such as + DIGEST-MD5, though this interface is subject to change + * Modified parseuser to deal better with default realms + * Simplified realm handling in DIGEST-MD5 (getrealm callback + is no longer required). + * Cleaned up some memory management issues in DIGEST-MD5 + +2001-07-19 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Fixed prototype of sasl_getpath_t to be in conformance with + memory allocation rules + * Fixed up samples directory + * Try to dlopen using information in .la file if available + (based on patch from + Stoned Elipot <Stoned.Elipot@script.jussieu.fr>) + * Resolution of most of the server-send-first and client-send-last + issues (using mechanism feature flags) + +2001-07-18 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Updated config.guess and config.sub + * Better underscore checking for dlsym + * Resolved possible global_utils namespace collision + * Updated sasldb library to be expandable to multiple properties + if the need arises in the future. + * IPv6 support from Hajimu UMEMOTO <ume@mahoroba.org> + +2001-07-17 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Extricated sasldb support to an auxprop plugin only. + sasldb modifications can now only be done through the saslpasswd + interface. + +2001-07-13 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Fixed buffer overrun problem in sasldb auxprop plugin + * Removed severe memory leak from testsuite + * Version 2.0.2-ALPHA Released + +2001-07-11 Rob Siemborski <rjs3+@andrew.cmu.edu> + * error reporting in KERBEROS_V4 plugin + * vague handling of SASL_AUTHSOURCE for getprop + * random misc error reporting bugs + * basic error messages for GSSAPI plugin + +2001-07-10 Rob Siemborski <rjs3+@andrew.cmu.edu> + * added client-send-first logic in glue code + * removed some client-send-first logic in mechanisms + * removed IPv4 specifics from sasl_conn_t + * Much gluecode error revamping (store the error code + in sasl_conn_t) + +2001-07-09 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Removed dependency on "name" in canonuser plugin structure + * Update configure.in from a new configure.scan + * Update copyright info in man pages, finished all API man pages + * Added auxprop tests to testsuite + * Added userdb callback support + +2001-07-09 Rob Siemborski <rjs3+@andrew.cmu.edu> + * First attempt at making the java code work again + * Minor memory and byte order bugfixes + * Added testing support for dmalloc (--with-dmalloc) + +2001-07-06 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Loading of auxprop and canonuser plugins from DSOs + (This still sucks performance wise, and will be fixed soon) + * Fixed some lack of indirection in the plugins + * Reverted to the v1 entry points for the plugins + * Cleaned up a good deal of the library loading code so it + now only gets called from the sasl_*_init functions, and + all the cleanup happens in the common sasl_done function + * Added SASL_IPREMOTEPORT and SASL_IPLOCALPORT to setprop, + and now _sasl_conn_init calls it to do the same work. + +2001-07-05 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Working libsfsasl and smtptest program (--with-sfio) + * Fixed sasldblistusers (atleast for Berkeley DB) + * seterror() calls in ANONYMOUS, CRAM, PLAIN and LOGIN + * Some new manpages + +2001-07-03 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Static library compilation now optional (--with-staticsasl) + Note that this is different from --enable-static, which causes + libtool to build static versions of everything is is almost + certainly NOT what you want. + * Removed all references to the ancient NANA code. + * Updated some documentation. + +2001-07-02 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Improved allocation efficiency of KERBEROS_V4, DIGEST-MD5, + and GSSAPI security layers. + * Fixed a decode bug in DIGEST-MD5 (and testsuite improvements to + help find similar ones) + * Fixed a number of solaris compiler warnings + * Static Library Build Support + +2001-06-30 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Cleanup of some man pages (added sasl_errors.3) + +2001-06-29 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Cleanup of APOP Code + new man page (Ken Murchison <ken@oceana.com>) + * Cleanup of comments in some files (Ken Murchison <ken@oceana.com>) + * Fixed some compiler errors on Solaris using /opt/SUNWspro/bin/cc + (Reported by Mei-Hui Su <mei@ISI.EDU> + +2001-06-28 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Improved memory allocation in default sasl_decode handler + * Added ability to disable sasl_checkapop (--disable-checkapop) + * Re-initialized kerberos mutex to NULL after it was freed + +2001-06-28 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Fixed a severe bug in DIGEST-MD5 Plugin + * KERBEROS_V4 plugin now thread safe + * Version 2.0.1-ALPHA Released (due to DIGEST-MD5 problem) + +2001-06-27 Rob Siemborski <rjs3+@andrew.cmu.edu> + * Version 2.0.0-ALPHA Released diff --git a/contrib/libs/sasl/INSTALL.TXT b/contrib/libs/sasl/INSTALL.TXT new file mode 100644 index 0000000000..703ff08e4c --- /dev/null +++ b/contrib/libs/sasl/INSTALL.TXT @@ -0,0 +1 @@ +For installation instructions, see doc/legacy/install.html. diff --git a/contrib/libs/sasl/README b/contrib/libs/sasl/README new file mode 100644 index 0000000000..b2e1dcc13b --- /dev/null +++ b/contrib/libs/sasl/README @@ -0,0 +1 @@ +Look at README.md diff --git a/contrib/libs/sasl/README.GS2 b/contrib/libs/sasl/README.GS2 new file mode 100644 index 0000000000..973cee883e --- /dev/null +++ b/contrib/libs/sasl/README.GS2 @@ -0,0 +1,3 @@ +To build the GS2 SASL mechanism, you need MIT Kerberos 1.9. + +Please e-mail lukeh@padl.com with any bug reports. diff --git a/contrib/libs/sasl/README.ldapdb b/contrib/libs/sasl/README.ldapdb new file mode 100644 index 0000000000..d632af3152 --- /dev/null +++ b/contrib/libs/sasl/README.ldapdb @@ -0,0 +1,53 @@ +ldapdb auxprop plugin is contributed to cyrus-sasl by the OpenLDAP Project +(http://www.openldap.org) under the license below. The code is written by +Howard Chu (hyc@openldap.org) and it is integrated to the cyrus-sasl +distribution by Igor Brezac (igor@ypass.net). See doc/options.html for the +plugin documentation. + +The OpenLDAP Public License + Version 2.8, 17 August 2003 + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions in source form must retain copyright statements + and notices, + +2. Redistributions in binary form must reproduce applicable copyright + statements and notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution, and + +3. Redistributions must contain a verbatim copy of this document. + +The OpenLDAP Foundation may revise this license from time to time. +Each revision is distinguished by a version number. You may use +this Software under terms of this license revision or under the +terms of any subsequent revision of the license. + +THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS +CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED 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 OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) +OR OWNER(S) OF THE SOFTWARE 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. + +The names of the authors and copyright holders must not be used in +advertising or otherwise to promote the sale, use or other dealing +in this Software without specific, written prior permission. Title +to copyright in this Software shall at all times remain with copyright +holders. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, +California, USA. All Rights Reserved. Permission to copy and +distribute verbatim copies of this document is granted. diff --git a/contrib/libs/sasl/README.md b/contrib/libs/sasl/README.md new file mode 100644 index 0000000000..9953921943 --- /dev/null +++ b/contrib/libs/sasl/README.md @@ -0,0 +1,27 @@ +[![Build Status:master](https://api.travis-ci.org/cyrusimap/cyrus-sasl.svg?branch=master)](https://travis-ci.org/cyrusimap/cyrus-sasl) + + +## Cyrus SASL +This is the Cyrus SASL API implementation. It can be used on the client +or server side to provide authentication and authorization services. +See RFC 4422 for more information. + +The latest version is available at: +https://github.com/cyrusimap/cyrus-sasl/releases + +There's a mailing list for Cyrus SASL. Subscribe by sending a message +to majordomo@lists.andrew.cmu.edu with the body "subscribe +cyrus-sasl". The mailing list is available via anonymous IMAP at +imap://cyrus.andrew.cmu.edu/archive.cyrus-sasl or via the web at +https://lists.andrew.cmu.edu/pipermail/cyrus-sasl/. + +If you are looking to port SASLv1 applications to SASLv2, please see +doc/appconvert.html + +Bugs can be searched/reported [on GitHub](https://github.com/cyrusimap/cyrus-sasl/issues), +but please also notify the mailing list. + +## DOCUMENTATION + +Please see doc/legacy/index.html for detailed documentation. + diff --git a/contrib/libs/sasl/README.release b/contrib/libs/sasl/README.release new file mode 100644 index 0000000000..55720b7277 --- /dev/null +++ b/contrib/libs/sasl/README.release @@ -0,0 +1,89 @@ +* Ensure version is incremented in: + configure.ac + docsrc/conf.py + include/sasl.h + win32/common.mak + win32/include/config.h + +* Add release notes to + docsrc/sasl/release-notes/2.1/index.rst + +* NOCONFIGURE=yes ./autogen.sh + to generate configure etc + +* MUST build with one of the supported database backends: + bdb gdbm lmdb ndbm + It is recommended to use lmdb + +Generally using Debian or Ubuntu distributions for the compile steps is useful +as they provide both MIT and Heimdal development packages: + + NOTE: Sphinx version 4.4.0 or later is required. It may be mandatory to install + it manually via python3-pip: + pip install -U Sphinx + OR + apt install sphinx-common (Documentation) + + Other dependencies: + apt install libpod-pom-view-restructured-perl (Documentation) + apt install liblmdb-dev (LMDB backend) + apt install libkrb5-dev (MIT Kerberos) + apt install heimdal-dev (Heimdal Kerberos) + +* Confirm build compiles with no kerberos support + ./configure --with-dblib=lmdb + make + +* Confirm build compiles with MIT kerberos support + LDFLAGS="-L/usr/lib/x86_64-linux-gnu/mit-krb5/" CPPFLAGS="-I/usr/include/mit-krb5" ./configure --with-dblib=lmdb + make + +* Confirm build compiles with Heimdal kerberos support + LDFLAGS="-L/usr/lib/x86_64-linux-gnu/heimdal/" CPPFLAGS="-I/usr/include/heimdal" ./configure --with-dblib=lmdb + make + +* If possible, build on a non-Linux OS such as FreeBSD + NOTE: Currently FreeBSD has ndbm installed as an alternative + to test with rather than LMDB + pkg install openssl + pkg install heimdal + +* git tag the repository: + git tag -a -s cyrus-sasl-MAJOR.MINOR.PATCH + + We are pleased to announce the release of Cyrus SASL version MAJOR.MINOR.PATCH. + + This release contains features and fixes you can find on the following pages: + + https://www.cyrusimap.org/sasl/sasl/release-notes/2.1/index.html#new-in-MAJOR-MINOR-PATCH + +* check out the tag: + git checkout cyrus-sasl-MAJOR.MINOR.PATCH + +* make distcheck + Generates the tarball etc + +* Post release announcement to announce@cyrus.topicbox.com + + Example: + + The Cyrus team is proud to announce the immediate availability of a new version of Cyrus SASL: MAJOR.MINOR.PATCH + + <INSERT HIGHLIGHTS OF THE RELEASE> + + Of course, please check the release notes for the full list of changes. + + Release notes: + + https://www.cyrusimap.org/sasl/sasl/release-notes/2.1/index.html#new-in-MAJOR-MINOR-PATCH + + Download URLs: + + https://github.com/cyrusimap/cyrus-sasl/releases/download/cyrus-sasl-MAJOR.MINOR.PATCH/cyrus-sasl-MAJOR.MINOR.PATCH.tar.gz + https://github.com/cyrusimap/cyrus-sasl/releases/download/cyrus-sasl-MAJOR.MINOR.PATCH/cyrus-sasl-MAJOR.MINOR.PATCH.tar.gz.sig + + On behalf of the Cyrus team, + + Kind regards, + + YOUR NAME HERE diff --git a/contrib/libs/sasl/common/plugin_common.c b/contrib/libs/sasl/common/plugin_common.c new file mode 100644 index 0000000000..94d4479245 --- /dev/null +++ b/contrib/libs/sasl/common/plugin_common.c @@ -0,0 +1,920 @@ +/* Generic SASL plugin utility functions + * Rob Siemborski + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#ifndef macintosh +#ifdef WIN32 +# include <winsock2.h> +# include <versionhelpers.h> +#else +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# include <netdb.h> +# include <sys/utsname.h> +#endif /* WIN32 */ +#endif /* macintosh */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <fcntl.h> +#include <sasl.h> +#include <saslutil.h> +#include <saslplug.h> + +#include <errno.h> +#include <ctype.h> +#include <stdio.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "plugin_common.h" + +/* translate IPv4 mapped IPv6 address to IPv4 address */ +static void sockaddr_unmapped( +#ifdef IN6_IS_ADDR_V4MAPPED + struct sockaddr *sa, socklen_t *len +#else + struct sockaddr *sa __attribute__((unused)), + socklen_t *len __attribute__((unused)) +#endif +) +{ +#ifdef IN6_IS_ADDR_V4MAPPED + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin4; + uint32_t addr; + int port; + + if (sa->sa_family != AF_INET6) + return; + sin6 = (struct sockaddr_in6 *)sa; + if (!IN6_IS_ADDR_V4MAPPED((&sin6->sin6_addr))) + return; + sin4 = (struct sockaddr_in *)sa; +#ifdef s6_addr32 + addr = *(uint32_t *)&sin6->sin6_addr.s6_addr32[3]; +#else + memcpy(&addr, &sin6->sin6_addr.s6_addr[12], 4); +#endif + port = sin6->sin6_port; + memset(sin4, 0, sizeof(struct sockaddr_in)); + sin4->sin_addr.s_addr = addr; + sin4->sin_port = port; + sin4->sin_family = AF_INET; +#ifdef HAVE_SOCKADDR_SA_LEN + sin4->sin_len = sizeof(struct sockaddr_in); +#endif + *len = sizeof(struct sockaddr_in); +#else + return; +#endif +} + +int _plug_ipfromstring(const sasl_utils_t *utils, const char *addr, + struct sockaddr *out, socklen_t outlen) +{ + int i, j; + socklen_t len; + struct sockaddr_storage ss; + struct addrinfo hints, *ai = NULL; + char hbuf[NI_MAXHOST]; + + if(!utils || !addr || !out) { + if(utils) PARAMERROR( utils ); + return SASL_BADPARAM; + } + + /* Parse the address */ + for (i = 0; addr[i] != '\0' && addr[i] != ';'; i++) { + if (i + 1 >= NI_MAXHOST) { + if(utils) PARAMERROR( utils ); + return SASL_BADPARAM; + } + hbuf[i] = addr[i]; + } + hbuf[i] = '\0'; + + if (addr[i] == ';') + i++; + /* XXX/FIXME: Do we need this check? */ + for (j = i; addr[j] != '\0'; j++) + if (!isdigit((int)(addr[j]))) { + PARAMERROR( utils ); + return SASL_BADPARAM; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + + if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0) { + PARAMERROR( utils ); + return SASL_BADPARAM; + } + + len = (socklen_t) ai->ai_addrlen; + memcpy(&ss, ai->ai_addr, len); + freeaddrinfo(ai); + sockaddr_unmapped((struct sockaddr *)&ss, &len); + if (outlen < len) { + PARAMERROR( utils ); + return SASL_BUFOVER; + } + + memcpy(out, &ss, len); + + return SASL_OK; +} + +int _plug_iovec_to_buf(const sasl_utils_t *utils, const struct iovec *vec, + unsigned numiov, buffer_info_t **output) +{ + unsigned i; + int ret; + buffer_info_t *out; + char *pos; + + if(!utils || !vec || !output) { + if(utils) PARAMERROR( utils ); + return SASL_BADPARAM; + } + + if(!(*output)) { + *output = utils->malloc(sizeof(buffer_info_t)); + if(!*output) { + MEMERROR(utils); + return SASL_NOMEM; + } + memset(*output,0,sizeof(buffer_info_t)); + } + + out = *output; + + out->curlen = 0; + for(i=0; i<numiov; i++) + out->curlen += vec[i].iov_len; + + ret = _plug_buf_alloc(utils, &out->data, &out->reallen, out->curlen); + + if(ret != SASL_OK) { + MEMERROR(utils); + return SASL_NOMEM; + } + + memset(out->data, 0, out->reallen); + pos = out->data; + + for(i=0; i<numiov; i++) { + memcpy(pos, vec[i].iov_base, vec[i].iov_len); + pos += vec[i].iov_len; + } + + return SASL_OK; +} + +/* Basically a conditional call to realloc(), if we need more */ +int _plug_buf_alloc(const sasl_utils_t *utils, char **rwbuf, + unsigned *curlen, unsigned newlen) +{ + if(!utils || !rwbuf || !curlen) { + if (utils) PARAMERROR(utils); + return SASL_BADPARAM; + } + + if(!(*rwbuf)) { + *rwbuf = utils->malloc(newlen); + if (*rwbuf == NULL) { + *curlen = 0; + MEMERROR(utils); + return SASL_NOMEM; + } + *curlen = newlen; + } else if(*rwbuf && *curlen < newlen) { + unsigned needed = 2*(*curlen); + + while(needed < newlen) + needed *= 2; + + *rwbuf = utils->realloc(*rwbuf, needed); + if (*rwbuf == NULL) { + *curlen = 0; + MEMERROR(utils); + return SASL_NOMEM; + } + *curlen = needed; + } + + return SASL_OK; +} + +/* copy a string */ +int _plug_strdup(const sasl_utils_t * utils, const char *in, + char **out, int *outlen) +{ + size_t len = 0; + + if(!utils || !in || !out) { + if(utils) PARAMERROR(utils); + return SASL_BADPARAM; + } + + len = strlen(in); + + *out = utils->malloc(len + 1); + if (!*out) { + MEMERROR(utils); + return SASL_NOMEM; + } + + strcpy((char *) *out, in); + + if (outlen) + *outlen = (int) len; + + return SASL_OK; +} + +void _plug_free_string(const sasl_utils_t *utils, char **str) +{ + size_t len; + + if (!utils || !str || !(*str)) return; + + len = strlen(*str); + + utils->erasebuffer(*str, (unsigned int) len); + utils->free(*str); + + *str=NULL; +} + +void _plug_free_secret(const sasl_utils_t *utils, sasl_secret_t **secret) +{ + if(!utils || !secret || !(*secret)) return; + + utils->erasebuffer((char *)(*secret)->data, (*secret)->len); + utils->free(*secret); + *secret = NULL; +} + +/* + * Trys to find the prompt with the lookingfor id in the prompt list + * Returns it if found. NULL otherwise + */ +sasl_interact_t *_plug_find_prompt(sasl_interact_t **promptlist, + unsigned int lookingfor) +{ + sasl_interact_t *prompt; + + if (promptlist && *promptlist) { + for (prompt = *promptlist; prompt->id != SASL_CB_LIST_END; ++prompt) { + if (prompt->id==lookingfor) + return prompt; + } + } + + return NULL; +} + +/* + * Retrieve the simple string given by the callback id. + */ +int _plug_get_simple(const sasl_utils_t *utils, unsigned int id, int required, + const char **result, sasl_interact_t **prompt_need) +{ + + int ret = SASL_FAIL; + sasl_getsimple_t *simple_cb; + void *simple_context; + sasl_interact_t *prompt; + + *result = NULL; + + /* see if we were given the result in the prompt */ + prompt = _plug_find_prompt(prompt_need, id); + if (prompt != NULL) { + /* We prompted, and got.*/ + + if (required && !prompt->result) { + SETERROR(utils, "Unexpectedly missing a prompt result in _plug_get_simple"); + return SASL_BADPARAM; + } + + *result = prompt->result; + return SASL_OK; + } + + /* Try to get the callback... */ + ret = utils->getcallback(utils->conn, id, (sasl_callback_ft *)&simple_cb, &simple_context); + + if (ret == SASL_FAIL && !required) + return SASL_OK; + + if (ret == SASL_OK && simple_cb) { + ret = simple_cb(simple_context, id, result, NULL); + if (ret != SASL_OK) + return ret; + + if (required && !*result) { + PARAMERROR(utils); + return SASL_BADPARAM; + } + } + + return ret; +} + +/* + * Retrieve the user password. + */ +int _plug_get_password(const sasl_utils_t *utils, sasl_secret_t **password, + unsigned int *iscopy, sasl_interact_t **prompt_need) +{ + int ret = SASL_FAIL; + sasl_getsecret_t *pass_cb; + void *pass_context; + sasl_interact_t *prompt; + + *password = NULL; + *iscopy = 0; + + /* see if we were given the password in the prompt */ + prompt = _plug_find_prompt(prompt_need, SASL_CB_PASS); + if (prompt != NULL) { + /* We prompted, and got.*/ + + if (!prompt->result) { + SETERROR(utils, "Unexpectedly missing a prompt result in _plug_get_password"); + return SASL_BADPARAM; + } + + /* copy what we got into a secret_t */ + *password = (sasl_secret_t *) utils->malloc(sizeof(sasl_secret_t) + + prompt->len + 1); + if (!*password) { + MEMERROR(utils); + return SASL_NOMEM; + } + + (*password)->len=prompt->len; + memcpy((*password)->data, prompt->result, prompt->len); + (*password)->data[(*password)->len]=0; + + *iscopy = 1; + + return SASL_OK; + } + + /* Try to get the callback... */ + ret = utils->getcallback(utils->conn, SASL_CB_PASS, + (sasl_callback_ft *)&pass_cb, &pass_context); + + if (ret == SASL_OK && pass_cb) { + ret = pass_cb(utils->conn, pass_context, SASL_CB_PASS, password); + if (ret != SASL_OK) + return ret; + + if (!*password) { + PARAMERROR(utils); + return SASL_BADPARAM; + } + } + + return ret; +} + +/* + * Retrieve the string given by the challenge prompt id. + */ +int _plug_challenge_prompt(const sasl_utils_t *utils, unsigned int id, + const char *challenge, const char *promptstr, + const char **result, sasl_interact_t **prompt_need) +{ + int ret = SASL_FAIL; + sasl_chalprompt_t *chalprompt_cb; + void *chalprompt_context; + sasl_interact_t *prompt; + + *result = NULL; + + /* see if we were given the password in the prompt */ + prompt = _plug_find_prompt(prompt_need, id); + if (prompt != NULL) { + /* We prompted, and got.*/ + + if (!prompt->result) { + SETERROR(utils, "Unexpectedly missing a prompt result in _plug_challenge_prompt"); + return SASL_BADPARAM; + } + + *result = prompt->result; + return SASL_OK; + } + + /* Try to get the callback... */ + ret = utils->getcallback(utils->conn, id, + (sasl_callback_ft *)&chalprompt_cb, &chalprompt_context); + + if (ret == SASL_OK && chalprompt_cb) { + ret = chalprompt_cb(chalprompt_context, id, + challenge, promptstr, NULL, result, NULL); + if (ret != SASL_OK) + return ret; + + if (!*result) { + PARAMERROR(utils); + return SASL_BADPARAM; + } + } + + return ret; +} + +/* + * Retrieve the client realm. + */ +int _plug_get_realm(const sasl_utils_t *utils, const char **availrealms, + const char **realm, sasl_interact_t **prompt_need) +{ + int ret = SASL_FAIL; + sasl_getrealm_t *realm_cb; + void *realm_context; + sasl_interact_t *prompt; + + *realm = NULL; + + /* see if we were given the result in the prompt */ + prompt = _plug_find_prompt(prompt_need, SASL_CB_GETREALM); + if (prompt != NULL) { + /* We prompted, and got.*/ + + if (!prompt->result) { + SETERROR(utils, "Unexpectedly missing a prompt result in _plug_get_realm"); + return SASL_BADPARAM; + } + + *realm = prompt->result; + return SASL_OK; + } + + /* Try to get the callback... */ + ret = utils->getcallback(utils->conn, SASL_CB_GETREALM, + (sasl_callback_ft *)&realm_cb, &realm_context); + + if (ret == SASL_OK && realm_cb) { + ret = realm_cb(realm_context, SASL_CB_GETREALM, availrealms, realm); + if (ret != SASL_OK) + return ret; + + if (!*realm) { + PARAMERROR(utils); + return SASL_BADPARAM; + } + } + + return ret; +} + +/* + * Make the requested prompts. (prompt==NULL means we don't want it) + */ +int _plug_make_prompts(const sasl_utils_t *utils, + sasl_interact_t **prompts_res, + const char *user_prompt, const char *user_def, + const char *auth_prompt, const char *auth_def, + const char *pass_prompt, const char *pass_def, + const char *echo_chal, + const char *echo_prompt, const char *echo_def, + const char *realm_chal, + const char *realm_prompt, const char *realm_def) +{ + int num = 1; + int alloc_size; + sasl_interact_t *prompts; + + if (user_prompt) num++; + if (auth_prompt) num++; + if (pass_prompt) num++; + if (echo_prompt) num++; + if (realm_prompt) num++; + + if (num == 1) { + SETERROR( utils, "make_prompts() called with no actual prompts" ); + return SASL_FAIL; + } + + alloc_size = sizeof(sasl_interact_t)*num; + prompts = utils->malloc(alloc_size); + if (!prompts) { + MEMERROR( utils ); + return SASL_NOMEM; + } + memset(prompts, 0, alloc_size); + + *prompts_res = prompts; + + if (user_prompt) { + (prompts)->id = SASL_CB_USER; + (prompts)->challenge = "Authorization Name"; + (prompts)->prompt = user_prompt; + (prompts)->defresult = user_def; + + prompts++; + } + + if (auth_prompt) { + (prompts)->id = SASL_CB_AUTHNAME; + (prompts)->challenge = "Authentication Name"; + (prompts)->prompt = auth_prompt; + (prompts)->defresult = auth_def; + + prompts++; + } + + if (pass_prompt) { + (prompts)->id = SASL_CB_PASS; + (prompts)->challenge = "Password"; + (prompts)->prompt = pass_prompt; + (prompts)->defresult = pass_def; + + prompts++; + } + + if (echo_prompt) { + (prompts)->id = SASL_CB_ECHOPROMPT; + (prompts)->challenge = echo_chal; + (prompts)->prompt = echo_prompt; + (prompts)->defresult = echo_def; + + prompts++; + } + + if (realm_prompt) { + (prompts)->id = SASL_CB_GETREALM; + (prompts)->challenge = realm_chal; + (prompts)->prompt = realm_prompt; + (prompts)->defresult = realm_def; + + prompts++; + } + + /* add the ending one */ + (prompts)->id = SASL_CB_LIST_END; + (prompts)->challenge = NULL; + (prompts)->prompt = NULL; + (prompts)->defresult = NULL; + + return SASL_OK; +} + +void _plug_decode_init(decode_context_t *text, + const sasl_utils_t *utils, unsigned int in_maxbuf) +{ + memset(text, 0, sizeof(decode_context_t)); + + text->utils = utils; + text->needsize = 4; + text->in_maxbuf = in_maxbuf; +} + +/* + * Decode as much of the input as possible (possibly none), + * using decode_pkt() to decode individual packets. + */ +int _plug_decode(decode_context_t *text, + const char *input, unsigned inputlen, + char **output, /* output buffer */ + unsigned *outputsize, /* current size of output buffer */ + unsigned *outputlen, /* length of data in output buffer */ + int (*decode_pkt)(void *rock, + const char *input, unsigned inputlen, + char **output, unsigned *outputlen), + void *rock) +{ + unsigned int tocopy; + unsigned diff; + char *tmp; + unsigned tmplen; + int ret; + + *outputlen = 0; + + while (inputlen) { /* more input */ + if (text->needsize) { /* need to get the rest of the 4-byte size */ + + /* copy as many bytes (up to 4) as we have into size buffer */ + tocopy = (inputlen > text->needsize) ? text->needsize : inputlen; + memcpy(text->sizebuf + 4 - text->needsize, input, tocopy); + text->needsize -= tocopy; + + input += tocopy; + inputlen -= tocopy; + + if (!text->needsize) { /* we have the entire 4-byte size */ + memcpy(&(text->size), text->sizebuf, 4); + text->size = ntohl(text->size); + text->cursize = 0; + } else { + /* We do NOT have the entire 4-byte size... + * wait for more data */ + return SASL_OK; + } + } + + if (!text->size) /* should never happen */ + return SASL_FAIL; + + if (text->size > text->in_maxbuf) { + text->utils->log(NULL, SASL_LOG_ERR, + "encoded packet size too big (%d > %d)", + text->size, text->in_maxbuf); + return SASL_FAIL; + } + + if (!text->buffer) { + text->buffer = text->utils->malloc(text->in_maxbuf); + if (text->buffer == NULL) return SASL_NOMEM; + } + + diff = text->size - text->cursize; /* bytes needed for full packet */ + + if (inputlen < diff) { /* not a complete packet, need more input */ + memcpy(text->buffer + text->cursize, input, inputlen); + text->cursize += inputlen; + return SASL_OK; + } + + /* copy the rest of the packet */ + memcpy(text->buffer + text->cursize, input, diff); + input += diff; + inputlen -= diff; + + /* decode the packet (no need to free tmp) */ + ret = decode_pkt(rock, text->buffer, text->size, &tmp, &tmplen); + if (ret != SASL_OK) return ret; + + /* append the decoded packet to the output */ + ret = _plug_buf_alloc(text->utils, output, outputsize, + *outputlen + tmplen + 1); /* +1 for NUL */ + if (ret != SASL_OK) return ret; + + memcpy(*output + *outputlen, tmp, tmplen); + *outputlen += tmplen; + + /* protect stupid clients */ + *(*output + *outputlen) = '\0'; + + /* reset for the next packet */ + text->needsize = 4; + } + + return SASL_OK; +} + +void _plug_decode_free(decode_context_t *text) +{ + if (text->buffer) text->utils->free(text->buffer); +} + +/* returns the realm we should pretend to be in */ +int _plug_parseuser(const sasl_utils_t *utils, + char **user, char **realm, const char *user_realm, + const char *serverFQDN, const char *input) +{ + int ret; + char *r; + + if(!user || !serverFQDN) { + PARAMERROR( utils ); + return SASL_BADPARAM; + } + + r = strchr(input, '@'); + if (!r) { + /* hmmm, the user didn't specify a realm */ + if(user_realm && user_realm[0]) { + ret = _plug_strdup(utils, user_realm, realm, NULL); + } else { + /* Default to serverFQDN */ + ret = _plug_strdup(utils, serverFQDN, realm, NULL); + } + + if (ret == SASL_OK) { + ret = _plug_strdup(utils, input, user, NULL); + } + } else { + r++; + ret = _plug_strdup(utils, r, realm, NULL); + *--r = '\0'; + *user = utils->malloc(r - input + 1); + if (*user) { + strncpy(*user, input, r - input +1); + } else { + MEMERROR( utils ); + ret = SASL_NOMEM; + } + *r = '@'; + } + + return ret; +} + +int _plug_make_fulluser(const sasl_utils_t *utils, + char **fulluser, + const char * useronly, + const char *realm) +{ + if(!fulluser || !useronly || !realm) { + PARAMERROR( utils ); + return (SASL_BADPARAM); + } + + *fulluser = utils->malloc (strlen(useronly) + strlen(realm) + 2); + if (*fulluser == NULL) { + MEMERROR( utils ); + return (SASL_NOMEM); + } + + strcpy (*fulluser, useronly); + strcat (*fulluser, "@"); + strcat (*fulluser, realm); + + return (SASL_OK); +} + +char * _plug_get_error_message (const sasl_utils_t *utils, +#ifdef WIN32 + DWORD error +#else + int error +#endif + ) +{ + char * return_value; +#ifdef WIN32 + LPVOID lpMsgBuf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + if (_plug_strdup (utils, lpMsgBuf, &return_value, NULL) != SASL_OK) { + return_value = NULL; + } + + LocalFree( lpMsgBuf ); +#else /* !WIN32 */ + if (_plug_strdup (utils, strerror(error), &return_value, NULL) != SASL_OK) { + return_value = NULL; + } +#endif /* WIN32 */ + return (return_value); +} + +void _plug_snprintf_os_info (char * osbuf, int osbuf_len) +{ +#ifdef WIN32 + char *sysname; + sysname = "Unknown Windows"; + +/* Let's suppose it's still compilable with win2k sdk. So define everythig missing */ +#ifndef _WIN32_WINNT_WINXP +# define _WIN32_WINNT_WINXP 0x0501 +#endif +#ifndef _WIN32_WINNT_WS03 +# define _WIN32_WINNT_WS03 0x0502 +#endif +#ifndef _WIN32_WINNT_WIN6 +# define _WIN32_WINNT_WIN6 0x0600 +#endif +#ifndef _WIN32_WINNT_VISTA +# define _WIN32_WINNT_VISTA 0x0600 +#endif +#ifndef _WIN32_WINNT_WS08 +# define _WIN32_WINNT_WS08 0x0600 +#endif +#ifndef _WIN32_WINNT_LONGHORN +# define _WIN32_WINNT_LONGHORN 0x0600 +#endif +#ifndef _WIN32_WINNT_WIN7 +# define _WIN32_WINNT_WIN7 0x0601 +#endif +#ifndef _WIN32_WINNT_WIN8 +# define _WIN32_WINNT_WIN8 0x0602 +#endif +#ifndef _WIN32_WINNT_WINBLUE +# define _WIN32_WINNT_WINBLUE 0x0603 +#endif +#ifndef _WIN32_WINNT_WIN10 +# define _WIN32_WINNT_WIN10 0x0A00 +#endif + + /* and use IsWindowsVersionOrGreater instead of convenient wrappers by the same reason */ + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN10), LOBYTE(_WIN32_WINNT_WIN10), 0)) { + sysname = "Windows 10 or greater"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0)) { + sysname = "Windows 8.1"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0)) { + sysname = "Windows 8"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1)) { + sysname = "Windows 7 SP1"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0)) { + sysname = "Windows 7"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2)) { + sysname = "Windows Vista SP2"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1)) { + sysname = "Windows Vista SP1"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0)) { + sysname = "Windows Vista"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3)) { + sysname = "Windows XP SP3"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2)) { + sysname = "Windows XP SP2"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1)) { + sysname = "Windows XP SP1"; + } else + if (IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0)) { + sysname = "Windows XP"; + } + + snprintf(osbuf, osbuf_len, "%s", sysname); + +#else /* !WIN32 */ + struct utsname os; + + uname(&os); + snprintf(osbuf, osbuf_len, "%s %s", os.sysname, os.release); +#endif /* WIN32 */ +} + +#if defined(WIN32) +unsigned int plug_sleep (unsigned int seconds) +{ + long dwSec = seconds*1000; + Sleep (dwSec); + return 0; +} +#endif diff --git a/contrib/libs/sasl/common/plugin_common.h b/contrib/libs/sasl/common/plugin_common.h new file mode 100644 index 0000000000..60f1dcd3a2 --- /dev/null +++ b/contrib/libs/sasl/common/plugin_common.h @@ -0,0 +1,230 @@ + +/* Generic SASL plugin utility functions + * Rob Siemborski + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PLUGIN_COMMON_H_ +#define _PLUGIN_COMMON_H_ + +#include <config.h> + +#ifndef macintosh +#ifdef WIN32 +# include <winsock2.h> +#else +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# include <netdb.h> +#endif /* WIN32 */ +#endif /* macintosh */ + +#include <sasl.h> +#include <saslutil.h> +#include <saslplug.h> + +#ifdef WIN32 +#define PLUG_API __declspec(dllexport) +#else +#define PLUG_API extern +#endif + +#define SASL_CLIENT_PLUG_INIT( x ) \ +extern sasl_client_plug_init_t x##_client_plug_init; \ +PLUG_API int sasl_client_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_client_plug_t **pluglist, \ + int *plugcount) { \ + return x##_client_plug_init(utils, maxversion, out_version, \ + pluglist, plugcount); \ +} + +#define SASL_SERVER_PLUG_INIT( x ) \ +extern sasl_server_plug_init_t x##_server_plug_init; \ +PLUG_API int sasl_server_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_server_plug_t **pluglist, \ + int *plugcount) { \ + return x##_server_plug_init(utils, maxversion, out_version, \ + pluglist, plugcount); \ +} + +#define SASL_AUXPROP_PLUG_INIT( x ) \ +extern sasl_auxprop_init_t x##_auxprop_plug_init; \ +PLUG_API int sasl_auxprop_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_auxprop_plug_t **plug, \ + const char *plugname) {\ + return x##_auxprop_plug_init(utils, maxversion, out_version, \ + plug, plugname); \ +} + +#define SASL_CANONUSER_PLUG_INIT( x ) \ +extern sasl_canonuser_init_t x##_canonuser_plug_init; \ +PLUG_API int sasl_canonuser_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_canonuser_plug_t **plug, \ + const char *plugname) {\ + return x##_canonuser_plug_init(utils, maxversion, out_version, \ + plug, plugname); \ +} + +/* note: msg cannot include additional variables, so if you want to + * do a printf-format string, then you need to call seterror yourself */ +#define SETERROR( utils, msg ) (utils)->seterror( (utils)->conn, 0, (msg) ) + +#ifndef MEMERROR +#define MEMERROR( utils ) \ + (utils)->seterror( (utils)->conn, 0, \ + "Out of Memory in " __FILE__ " near line %d", __LINE__ ) +#endif + +#ifndef PARAMERROR +#define PARAMERROR( utils ) \ + (utils)->seterror( (utils)->conn, 0, \ + "Parameter Error in " __FILE__ " near line %d", __LINE__ ) +#endif + +#ifndef SASLINT_H +typedef struct buffer_info +{ + char *data; + unsigned curlen; /* Current length of data in buffer */ + unsigned reallen; /* total length of buffer (>= curlen) */ +} buffer_info_t; + +#ifndef HAVE_GETHOSTNAME +#ifdef sun +/* gotta define gethostname ourselves on suns */ +extern int gethostname(char *, int); +#endif +#endif /* HAVE_GETHOSTNAME */ + +#endif /* SASLINT_H */ + +#ifdef __cplusplus +extern "C" { +#endif + +int _plug_ipfromstring(const sasl_utils_t *utils, const char *addr, + struct sockaddr *out, socklen_t outlen); +int _plug_iovec_to_buf(const sasl_utils_t *utils, const struct iovec *vec, + unsigned numiov, buffer_info_t **output); +int _plug_buf_alloc(const sasl_utils_t *utils, char **rwbuf, + unsigned *curlen, unsigned newlen); +int _plug_strdup(const sasl_utils_t * utils, const char *in, + char **out, int *outlen); +void _plug_free_string(const sasl_utils_t *utils, char **str); +void _plug_free_secret(const sasl_utils_t *utils, sasl_secret_t **secret); + +#define _plug_get_userid(utils, result, prompt_need) \ + _plug_get_simple(utils, SASL_CB_USER, 0, result, prompt_need) +#define _plug_get_authid(utils, result, prompt_need) \ + _plug_get_simple(utils, SASL_CB_AUTHNAME, 1, result, prompt_need) +int _plug_get_simple(const sasl_utils_t *utils, unsigned int id, int required, + const char **result, sasl_interact_t **prompt_need); + +int _plug_get_password(const sasl_utils_t *utils, sasl_secret_t **secret, + unsigned int *iscopy, sasl_interact_t **prompt_need); + +int _plug_challenge_prompt(const sasl_utils_t *utils, unsigned int id, + const char *challenge, const char *promptstr, + const char **result, sasl_interact_t **prompt_need); + +int _plug_get_realm(const sasl_utils_t *utils, const char **availrealms, + const char **realm, sasl_interact_t **prompt_need); + +int _plug_make_prompts(const sasl_utils_t *utils, + sasl_interact_t **prompts_res, + const char *user_prompt, const char *user_def, + const char *auth_prompt, const char *auth_def, + const char *pass_prompt, const char *pass_def, + const char *echo_chal, + const char *echo_prompt, const char *echo_def, + const char *realm_chal, + const char *realm_prompt, const char *realm_def); + +typedef struct decode_context { + const sasl_utils_t *utils; + unsigned int needsize; /* How much of the 4-byte size do we need? */ + char sizebuf[4]; /* Buffer to accumulate the 4-byte size */ + unsigned int size; /* Absolute size of the encoded packet */ + char *buffer; /* Buffer to accumulate an encoded packet */ + unsigned int cursize; /* Amount of packet data in the buffer */ + unsigned int in_maxbuf; /* Maximum allowed size of an incoming encoded packet */ +} decode_context_t; + +void _plug_decode_init(decode_context_t *text, + const sasl_utils_t *utils, unsigned int in_maxbuf); + +int _plug_decode(decode_context_t *text, + const char *input, unsigned inputlen, + char **output, unsigned *outputsize, unsigned *outputlen, + int (*decode_pkt)(void *rock, + const char *input, unsigned inputlen, + char **output, unsigned *outputlen), + void *rock); + +void _plug_decode_free(decode_context_t *text); + +int _plug_parseuser(const sasl_utils_t *utils, + char **user, char **realm, const char *user_realm, + const char *serverFQDN, const char *input); + +int _plug_make_fulluser(const sasl_utils_t *utils, + char **fulluser, const char * useronly, const char *realm); + +char * _plug_get_error_message (const sasl_utils_t *utils, +#ifdef WIN32 + DWORD error +#else + int error +#endif + ); +void _plug_snprintf_os_info (char * osbuf, int osbuf_len); + +#ifdef __cplusplus +} +#endif + +#endif /* _PLUGIN_COMMON_H_ */ diff --git a/contrib/libs/sasl/config-linux.h b/contrib/libs/sasl/config-linux.h new file mode 100644 index 0000000000..6528cc867f --- /dev/null +++ b/contrib/libs/sasl/config-linux.h @@ -0,0 +1,782 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + + +/* acconfig.h - autoheader configuration input */ +/* + * Copyright (c) 1998-2003 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H +#define CONFIG_H + + +/* Include SASLdb Support */ +/* #undef AUTH_SASLDB */ + +/* Do we need a leading _ for dlsym? */ +/* #undef DLSYM_NEEDS_UNDERSCORE */ + +/* Should we build a shared plugin (via dlopen) library? */ +/* #undef DO_DLOPEN */ + +/* should we support sasl_checkapop? */ +#define DO_SASL_CHECKAPOP /**/ + +/* should we support setpass() for SRP? */ +/* #undef DO_SRP_SETPASS */ + +/* Define if your getpwnam_r()/getspnam_r() functions take 5 arguments */ +#define GETXXNAM_R_5ARG 1 + +/* should we mutex-wrap calls into the GSS library? */ +/* #undef GSS_USE_MUTEXES */ + +/* Enable 'alwaystrue' password verifier? */ +/* #undef HAVE_ALWAYSTRUE */ + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* Include support for Courier's authdaemond? */ +#define HAVE_AUTHDAEMON /**/ + +/* Define to 1 if you have the <crypt.h> header file. */ +#define HAVE_CRYPT_H 1 + +/* Define to 1 if you have the <des.h> header file. */ +/* #undef HAVE_DES_H */ + +/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `dns_lookup' function. */ +/* #undef HAVE_DNS_LOOKUP */ + +/* Define to 1 if you have the `dn_expand' function. */ +#define HAVE_DN_EXPAND 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Do we have a getaddrinfo? */ +#define HAVE_GETADDRINFO /**/ + +/* Define to 1 if you have the `getdomainname' function. */ +#define HAVE_GETDOMAINNAME 1 + +/* Define to 1 if you have the `gethostname' function. */ +#define HAVE_GETHOSTNAME 1 + +/* Do we have a getnameinfo() function? */ +#define HAVE_GETNAMEINFO /**/ + +/* Define to 1 if you have the `getpassphrase' function. */ +/* #undef HAVE_GETPASSPHRASE */ + +/* Define to 1 if you have the `getpwnam' function. */ +#define HAVE_GETPWNAM 1 + +/* Define to 1 if you have the `getspnam' function. */ +#define HAVE_GETSPNAM 1 + +/* do we have getsubopt()? */ +#define HAVE_GETSUBOPT /**/ + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Include GSSAPI/Kerberos 5 Support */ +/* #undef HAVE_GSSAPI */ + +/* Define to 1 if you have the <gssapi/gssapi_ext.h> header file. */ +/* #undef HAVE_GSSAPI_GSSAPI_EXT_H */ + +/* Define if you have the gssapi/gssapi.h header file */ +/* #undef HAVE_GSSAPI_GSSAPI_H */ + +/* Define to 1 if you have the <gssapi/gssapi_krb5.h> header file. */ +/* #undef HAVE_GSSAPI_GSSAPI_KRB5_H */ + +/* Define if you have the gssapi.h header file */ +/* #undef HAVE_GSSAPI_H */ + +/* Define if your GSSAPI implementation defines + gsskrb5_register_acceptor_identity */ +/* #undef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY */ + +/* Define if your GSSAPI implementation defines GSS_C_NT_HOSTBASED_SERVICE */ +/* #undef HAVE_GSS_C_NT_HOSTBASED_SERVICE */ + +/* Define if your GSSAPI implementation defines GSS_C_NT_USER_NAME */ +/* #undef HAVE_GSS_C_NT_USER_NAME */ + +/* Define if your GSSAPI implementation defines GSS_C_SEC_CONTEXT_SASL_SSF */ +/* #undef HAVE_GSS_C_SEC_CONTEXT_SASL_SSF */ + +/* Define to 1 if you have the `gss_decapsulate_token' function. */ +/* #undef HAVE_GSS_DECAPSULATE_TOKEN */ + +/* Define to 1 if you have the `gss_encapsulate_token' function. */ +/* #undef HAVE_GSS_ENCAPSULATE_TOKEN */ + +/* Define to 1 if you have the `gss_get_name_attribute' function. */ +/* #undef HAVE_GSS_GET_NAME_ATTRIBUTE */ + +/* Define if your GSSAPI implementation defines gss_inquire_sec_context_by_oid + */ +/* #undef HAVE_GSS_INQUIRE_SEC_CONTEXT_BY_OID */ + +/* Define to 1 if you have the `gss_oid_equal' function. */ +/* #undef HAVE_GSS_OID_EQUAL */ + +/* Define if your GSSAPI implementation supports SPNEGO */ +/* #undef HAVE_GSS_SPNEGO */ + +/* Include HTTP form Support */ +/* #undef HAVE_HTTPFORM */ + +/* Define to 1 if you have the `inet_aton' function. */ +#define HAVE_INET_ATON 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `jrand48' function. */ +#define HAVE_JRAND48 1 + +/* Do we have Kerberos 4 Support? */ +/* #undef HAVE_KRB */ + +/* Define to 1 if you have the <krb5.h> header file. */ +/* #undef HAVE_KRB5_H */ + +/* Define to 1 if you have the `krb_get_err_text' function. */ +/* #undef HAVE_KRB_GET_ERR_TEXT */ + +/* Define to 1 if you have the <lber.h> header file. */ +/* #undef HAVE_LBER_H */ + +/* Support for LDAP? */ +/* #undef HAVE_LDAP */ + +/* Define to 1 if you have the <ldap.h> header file. */ +/* #undef HAVE_LDAP_H */ + +/* Define to 1 if you have the `resolv' library (-lresolv). */ +#define HAVE_LIBRESOLV 1 + +/* Define to 1 if you have the <limits.h> header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the <malloc.h> header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the `memmem' function. */ +#define HAVE_MEMMEM 1 + +/* Define to 1 if you have the <minix/config.h> header file. */ +/* #undef HAVE_MINIX_CONFIG_H */ + +/* Define to 1 if you have the `mkdir' function. */ +#define HAVE_MKDIR 1 + +/* Do we have mysql support? */ +/* #undef HAVE_MYSQL */ + +/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Do we have OpenSSL? */ +#define HAVE_OPENSSL /**/ + +/* Use OPIE for server-side OTP? */ +/* #undef HAVE_OPIE */ + +/* Support for PAM? */ +#define HAVE_PAM /**/ + +/* Define to 1 if you have the <paths.h> header file. */ +#define HAVE_PATHS_H 1 + +/* Do we have Postgres support? */ +/* #undef HAVE_PGSQL */ + +/* Include Support for pwcheck daemon? */ +/* #undef HAVE_PWCHECK */ + +/* Include support for saslauthd? */ +#define HAVE_SASLAUTHD /**/ + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Do we have SHA512? */ +#define HAVE_SHA512 /**/ + +/* Include SIA Support */ +/* #undef HAVE_SIA */ + +/* Does the system have snprintf()? */ +#define HAVE_SNPRINTF /**/ + +/* Does sockaddr have an sa_len? */ +/* #undef HAVE_SOCKADDR_SA_LEN */ + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Do we have a socklen_t? */ +#define HAVE_SOCKLEN_T /**/ + +/* Do we have SQLite support? */ +/* #undef HAVE_SQLITE */ + +/* Do we have SQLite3 support? */ +/* #undef HAVE_SQLITE3 */ + +/* Is there an ss_family in sockaddr_storage? */ +#define HAVE_SS_FAMILY /**/ + +/* Define to 1 if you have the <stdarg.h> header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdio.h> header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcat' function. */ +/* #undef HAVE_STRLCAT */ + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef HAVE_STRLCPY */ + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Define to 1 if you have the `strstr' function. */ +#define HAVE_STRSTR 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Do we have struct sockaddr_stroage? */ +#define HAVE_STRUCT_SOCKADDR_STORAGE /**/ + +/* Define to 1 if you have the <sysexits.h> header file. */ +#define HAVE_SYSEXITS_H 1 + +/* Define to 1 if you have the `syslog' function. */ +#define HAVE_SYSLOG 1 + +/* Define to 1 if you have the <syslog.h> header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the <sys/file.h> header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <sys/uio.h> header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the <varargs.h> header file. */ +/* #undef HAVE_VARARGS_H */ + +/* Does the system have vsnprintf()? */ +#define HAVE_VSNPRINTF /**/ + +/* Define to 1 if you have the <wchar.h> header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the <ws2tcpip.h> header file. */ +/* #undef HAVE_WS2TCPIP_H */ + +/* Should we keep handle to DB open in SASLDB plugin? */ +/* #undef KEEP_DB_OPEN */ + +/* Ignore IP Address in Kerberos 4 tickets? */ +/* #undef KRB4_IGNORE_IP_ADDRESS */ + +/* Using Heimdal */ +/* #undef KRB5_HEIMDAL */ + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "cyrus-sasl" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "https://github.com/cyrusimap/cyrus-sasl/issues" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "cyrus-sasl" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "cyrus-sasl 2.1.28" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "cyrus-sasl" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "https://www.cyrusimap.org" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.1.28" + +/* Where do we look for Courier authdaemond's socket? */ +#define PATH_AUTHDAEMON_SOCKET "/dev/null" + +/* Where do we look for saslauthd's socket? */ +#define PATH_SASLAUTHD_RUNDIR "/run/saslauthd" + +/* Force a preferred mechanism */ +/* #undef PREFER_MECH */ + +/* Location of pwcheck socket */ +/* #undef PWCHECKDIR */ + +/* Use BerkeleyDB for SASLdb */ +/* #undef SASL_BERKELEYDB */ + +/* Path to default SASLdb database */ +#define SASL_DB_PATH "/etc/sasldb2" + +/* File to use for source of randomness */ +#define SASL_DEV_RANDOM "/dev/urandom" + +/* Use GDBM for SASLdb */ +/* #undef SASL_GDBM */ + +/* Use LMDB for SASLdb */ +/* #undef SASL_LMDB */ + +/* Use NDBM for SASLdb */ +/* #undef SASL_NDBM */ + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 8 + +/* Link ANONYMOUS Statically */ +#define STATIC_ANONYMOUS /**/ + +/* Link CRAM-MD5 Statically */ +#define STATIC_CRAMMD5 /**/ + +/* Link DIGEST-MD5 Statically */ +#define STATIC_DIGESTMD5 /**/ + +/* Link GSSAPI Statically */ +/* #undef STATIC_GSSAPIV2 */ + +/* User KERBEROS_V4 Staticly */ +/* #undef STATIC_KERBEROS4 */ + +/* Link ldapdb plugin Statically */ +/* #undef STATIC_LDAPDB */ + +/* Link LOGIN Statically */ +/* #undef STATIC_LOGIN */ + +/* Link NTLM Statically */ +/* #undef STATIC_NTLM */ + +/* Link OTP Statically */ +#define STATIC_OTP /**/ + +/* Link PASSDSS Statically */ +/* #undef STATIC_PASSDSS */ + +/* Link PLAIN Staticly */ +#define STATIC_PLAIN /**/ + +/* Link SASLdb Staticly */ +#define STATIC_SASLDB /**/ + +/* Link SCRAM Statically */ +#define STATIC_SCRAM /**/ + +/* Link SQL plugin statically */ +/* #undef STATIC_SQL */ + +/* Link SRP Statically */ +/* #undef STATIC_SRP */ + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#define STDC_HEADERS 1 + +/* Should we try to dlopen() plugins while statically compiled? */ +/* #undef TRY_DLOPEN_WHEN_STATIC */ + +/* use the doors IPC API for saslauthd? */ +/* #undef USE_DOORS */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable general extensions on macOS. */ +#ifndef _DARWIN_C_SOURCE +# define _DARWIN_C_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable X/Open compliant socket functions that do not require linking + with -lxnet on HP-UX 11.11. */ +#ifndef _HPUX_ALT_XOPEN_SOCKET_API +# define _HPUX_ALT_XOPEN_SOCKET_API 1 +#endif +/* Identify the host operating system as Minix. + This macro does not affect the system headers' behavior. + A future release of Autoconf may stop defining this macro. */ +#ifndef _MINIX +/* # undef _MINIX */ +#endif +/* Enable general extensions on NetBSD. + Enable NetBSD compatibility extensions on Minix. */ +#ifndef _NETBSD_SOURCE +# define _NETBSD_SOURCE 1 +#endif +/* Enable OpenBSD compatibility extensions on NetBSD. + Oddly enough, this does nothing on OpenBSD. */ +#ifndef _OPENBSD_SOURCE +# define _OPENBSD_SOURCE 1 +#endif +/* Define to 1 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_SOURCE +/* # undef _POSIX_SOURCE */ +#endif +/* Define to 2 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_1_SOURCE +/* # undef _POSIX_1_SOURCE */ +#endif +/* Enable POSIX-compatible threading on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ +#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ +# define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ +#ifndef __STDC_WANT_IEC_60559_BFP_EXT__ +# define __STDC_WANT_IEC_60559_BFP_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ +#ifndef __STDC_WANT_IEC_60559_DFP_EXT__ +# define __STDC_WANT_IEC_60559_DFP_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ +#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ +# define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ +#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ +# define __STDC_WANT_IEC_60559_TYPES_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ +#ifndef __STDC_WANT_LIB_EXT2__ +# define __STDC_WANT_LIB_EXT2__ 1 +#endif +/* Enable extensions specified by ISO/IEC 24747:2009. */ +#ifndef __STDC_WANT_MATH_SPEC_FUNCS__ +# define __STDC_WANT_MATH_SPEC_FUNCS__ 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable X/Open extensions. Define to 500 only if necessary + to make mbstate_t available. */ +#ifndef _XOPEN_SOURCE +/* # undef _XOPEN_SOURCE */ +#endif + + +/* Version number of package */ +#define VERSION "2.1.28" + +/* Use DES */ +#define WITH_DES /**/ + +/* Linking against dmalloc? */ +/* #undef WITH_DMALLOC */ + +/* Use RC4 */ +#define WITH_RC4 /**/ + +/* Use OpenSSL DES Implementation */ +#define WITH_SSL_DES /**/ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef mode_t */ + +/* Define as a signed integer type capable of holding a process identifier. */ +/* #undef pid_t */ + + + +#define RETSIGTYPE void + +/* Create a struct iovec if we need one */ +#if !defined(_WIN32) +#if !defined(HAVE_SYS_UIO_H) +/* (win32 is handled in sasl.h) */ +struct iovec { + char *iov_base; + long iov_len; +}; +#else +#include <sys/types.h> +#include <sys/uio.h> +#endif +#endif + +/* location of the random number generator */ +#ifdef DEV_RANDOM +/* #undef DEV_RANDOM */ +#endif +#define DEV_RANDOM SASL_DEV_RANDOM + +/* if we've got krb_get_err_txt, we might as well use it; + especially since krb_err_txt isn't in some newer distributions + (MIT Kerb for Mac 4 being a notable example). If we don't have + it, we fall back to the krb_err_txt array */ +#ifdef HAVE_KRB_GET_ERR_TEXT +#define get_krb_err_txt krb_get_err_text +#else +#define get_krb_err_txt(X) (krb_err_txt[(X)]) +#endif + +/* Make Solaris happy... */ +#ifndef __EXTENSIONS__ +#define __EXTENSIONS__ 1 +#endif + +/* Make Linux happy... */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + +#define SASL_PATH_ENV_VAR "SASL_PATH" +#define SASL_CONF_PATH_ENV_VAR "SASL_CONF_PATH" + +#include <stdlib.h> +#include <sys/types.h> +#ifndef WIN32 +# include <sys/socket.h> +# include <netdb.h> +# include <netinet/in.h> +# ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +# endif +#else /* WIN32 */ +# include <winsock2.h> +#endif /* WIN32 */ +#include <string.h> + +#ifndef HAVE_SOCKLEN_T +typedef unsigned int socklen_t; +#endif /* HAVE_SOCKLEN_T */ + +#if !defined(HAVE_STRUCT_SOCKADDR_STORAGE) && !defined(WIN32) +#define _SS_MAXSIZE 128 /* Implementation specific max size */ +#define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr)) + +struct sockaddr_storage { + struct sockaddr ss_sa; + char __ss_pad2[_SS_PADSIZE]; +}; +# define ss_family ss_sa.sa_family +#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +#ifndef AF_INET6 +/* Define it to something that should never appear */ +#define AF_INET6 AF_MAX +#endif + +#ifndef HAVE_GETADDRINFO +#define getaddrinfo sasl_getaddrinfo +#define freeaddrinfo sasl_freeaddrinfo +#define gai_strerror sasl_gai_strerror +#endif + +#ifndef HAVE_GETNAMEINFO +#define getnameinfo sasl_getnameinfo +#endif + +#if !defined(HAVE_GETNAMEINFO) || !defined(HAVE_GETADDRINFO) +#include "gai.h" +#endif + +#ifndef AI_NUMERICHOST /* support glibc 2.0.x */ +#define AI_NUMERICHOST 4 +#define NI_NUMERICHOST 2 +#define NI_NAMEREQD 4 +#define NI_NUMERICSERV 8 +#endif + +#ifndef HAVE_SYSEXITS_H +#include "exits.h" +#else +#include "sysexits.h" +#endif + +/* Get the correct time.h */ +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#ifndef HIER_DELIMITER +#define HIER_DELIMITER '/' +#endif + +#ifdef WIN32 +#define SASL_ROOT_KEY "SOFTWARE\\Carnegie Mellon\\Project Cyrus\\SASL Library" +#define SASL_PLUGIN_PATH_ATTR "SearchPath" +#define SASL_CONF_PATH_ATTR "ConfFile" + +#include <windows.h> +inline static unsigned int sleep(unsigned int seconds) { + Sleep(seconds * 1000); + return 0; +} +#endif + +/* handy string manipulation functions */ +#ifndef HAVE_STRLCPY +extern size_t saslauthd_strlcpy(char *dst, const char *src, size_t len); +#define strlcpy(x,y,z) saslauthd_strlcpy((x),(y),(z)) +#endif +#ifndef HAVE_STRLCAT +extern size_t saslauthd_strlcat(char *dst, const char *src, size_t len); +#define strlcat(x,y,z) saslauthd_strlcat((x),(y),(z)) +#endif +#ifndef HAVE_ASPRINTF +extern int asprintf(char **str, const char *fmt, ...); +#endif + +#endif /* CONFIG_H */ + + +#if defined __GNUC__ && __GNUC__ > 6 + #define GCC_FALLTHROUGH __attribute__((fallthrough)); +#else + #define GCC_FALLTHROUGH /* fall through */ +#endif + diff --git a/contrib/libs/sasl/config-osx.h b/contrib/libs/sasl/config-osx.h new file mode 100644 index 0000000000..3d16804aee --- /dev/null +++ b/contrib/libs/sasl/config-osx.h @@ -0,0 +1,3 @@ +#pragma once + +#include "config-linux.h" diff --git a/contrib/libs/sasl/config-win.h b/contrib/libs/sasl/config-win.h new file mode 100644 index 0000000000..42fc50ada5 --- /dev/null +++ b/contrib/libs/sasl/config-win.h @@ -0,0 +1,221 @@ +/* config.h--SASL configuration for win32 + * Ryan Troll + */ +/* + * Copyright (c) 1998-2004 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#define _CRT_RAND_S + +#include <stddef.h> + +/* winsock2 includes windows.h. + Note that we can't include both winsock.h and winsock2.h as + they conflict */ +#include <winsock2.h> + +/* Our package */ +#define PACKAGE "cyrus-sasl" + +/* Our version */ +#define VERSION "2.1.28" + +/* Visual Studio supports prototypes */ +#define PROTOTYPES 1 + +#ifndef HAVE_CADDR_T +#ifndef caddr_t +typedef unsigned char *caddr_t; +#define HAVE_CADDR_T 1 +#endif +#endif + +#ifndef _INTPTR_T_DEFINED + +#ifdef _WIN64 +typedef __int64 intptr_t; +#else +typedef int intptr_t; +#endif + +#endif + +/* Registry key that contains the locations of the plugins */ +#define SASL_ROOT_KEY "SOFTWARE\\Carnegie Mellon\\Project Cyrus\\SASL Library" +#define SASL_PLUGIN_PATH_ATTR "SearchPath" +#define SASL_CONF_PATH_ATTR "ConfFile" + +/* : This should probably be replaced with a call to a function + : that gets the proper value from Registry */ +#define SASL_DB_PATH "c:\\CMU\\sasldb2" + +/* what db package are we using? */ +/* #undef SASL_GDBM */ +/* #undef SASL_NDBM */ +/* #undef SASL_BERKELEYDB */ + +/* which mechs can we link staticly? */ +#define STATIC_ANONYMOUS 1 +#define STATIC_CRAMMD5 1 +#define STATIC_DIGESTMD5 1 +#define STATIC_SCRAM 1 +#define STATIC_GSSAPIV2 1 +/* #undef STATIC_KERBEROS4 */ +#define STATIC_LOGIN 1 +/* #undef STATIC_MYSQL */ +#define STATIC_OTP 1 +#define STATIC_PLAIN 1 +#define STATIC_SASLDB 1 +#define STATIC_SRP 1 + +/* ------------------------------------------------------------ */ + +/* Things that are fetched via autoconf under Unix + */ +#define HAVE_MEMCPY 1 + +#define PLUGINDIR "C:\\CMU\\bin\\sasl2" +#define CONFIGDIR "C:\\CMU\\bin\\sasl2" + +/* Windows calls these functions something else + */ +#define strcasecmp stricmp +#define strncasecmp strnicmp + +#define MAXHOSTNAMELEN 1024 + +/* ------------------------------------------------------------ */ + +#define WITHOUT_NANA +#define L_DEFAULT_GUARD (0) +#define I_DEFAULT_GUARD (0) +#define I(foo) +#define VL(foo) printf foo; +#define VLP(foo,bar) + +#if !defined(__clang__) +/* we're not gcc */ +#define __attribute__(foo) +#endif + +/* : Same as in tpipv6.h */ +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif /* HAVE_SOCKLEN_T */ + +/* If we expect to run on XP and later, we have IPv6 support natively */ +#if TARGET_WIN_SYSTEM >= 51 +#if !defined(_WIN32_WINNT) +/* This forces the inclusion of OS supported functions, with no fallback */ +#define _WIN32_WINNT 0x0510 +#endif +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1300) +/* The following two defines will prevent our own definitions below */ +#define HAVE_GETADDRINFO +#define HAVE_GETNAMEINFO +#define HAVE_STRUCT_SOCKADDR_STORAGE +/* Unless _WIN32_WINNT > 0x0500, Ws2tcpip.h will try to find OS provided + getaddrinfo at runtime. It will fallback to Microsoft emulation, + if not found */ +#include <Ws2tcpip.h> +#endif + +#if !defined(HAVE_STRUCT_SOCKADDR_STORAGE) && !defined(_SS_MAXSIZE) +#define _SS_MAXSIZE 128 /* Implementation specific max size */ +#define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr)) + +struct sockaddr_storage { + struct sockaddr ss_sa; + char __ss_pad2[_SS_PADSIZE]; +}; +# define ss_family ss_sa.sa_family +#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +#ifndef AF_INET6 +/* Define it to something that should never appear */ +#define AF_INET6 AF_MAX +#endif + +#ifndef HAVE_GETADDRINFO +#define getaddrinfo sasl_getaddrinfo +#define freeaddrinfo sasl_freeaddrinfo +#define gai_strerror sasl_gai_strerror +#endif + +#ifndef HAVE_GETNAMEINFO +#define getnameinfo sasl_getnameinfo +#endif + +#if !defined(HAVE_GETNAMEINFO) || !defined(HAVE_GETADDRINFO) +#include "gai.h" +#endif + +#ifndef AI_NUMERICHOST /* support glibc 2.0.x */ +#define AI_NUMERICHOST 4 +#define NI_NUMERICHOST 2 +#define NI_NAMEREQD 4 +#define NI_NUMERICSERV 8 +#endif + +#include <time.h> + +/* Keep in sync with SleepyCat definitions */ +typedef int int32_t; +typedef __int64 int64_t; +#ifdef _WIN64 +typedef int64_t ssize_t; +#else +typedef int32_t ssize_t; +#endif + +#define HIER_DELIMITER '\\' + +#ifndef sleep +#define sleep(seconds) plug_sleep(seconds) +unsigned int plug_sleep(unsigned int seconds); +#endif + +#define GCC_FALLTHROUGH + +#endif /* CONFIG_H */ diff --git a/contrib/libs/sasl/config.h b/contrib/libs/sasl/config.h new file mode 100644 index 0000000000..1614a4f2b6 --- /dev/null +++ b/contrib/libs/sasl/config.h @@ -0,0 +1,9 @@ +#pragma once + +#if defined(__APPLE__) +# include "config-osx.h" +#elif defined(_MSC_VER) +# include "config-win.h" +#else +# include "config-linux.h" +#endif diff --git a/contrib/libs/sasl/include/exits.h b/contrib/libs/sasl/include/exits.h new file mode 100644 index 0000000000..464cb11bab --- /dev/null +++ b/contrib/libs/sasl/include/exits.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)sysexits.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _SYSEXITS_H_ +#define _SYSEXITS_H_ + +/* + * SYSEXITS.H -- Exit status codes for system programs. + * + * This include file attempts to categorize possible error + * exit statuses for system programs, notably delivermail + * and the Berkeley network. + * + * Error numbers begin at EX__BASE to reduce the possibility of + * clashing with other exit statuses that random programs may + * already return. The meaning of the codes is approximately + * as follows: + * + * EX_USAGE -- The command was used incorrectly, e.g., with + * the wrong number of arguments, a bad flag, a bad + * syntax in a parameter, or whatever. + * EX_DATAERR -- The input data was incorrect in some way. + * This should only be used for user's data & not + * system files. + * EX_NOINPUT -- An input file (not a system file) did not + * exist or was not readable. This could also include + * errors like "No message" to a mailer (if it cared + * to catch it). + * EX_NOUSER -- The user specified did not exist. This might + * be used for mail addresses or remote logins. + * EX_NOHOST -- The host specified did not exist. This is used + * in mail addresses or network requests. + * EX_UNAVAILABLE -- A service is unavailable. This can occur + * if a support program or file does not exist. This + * can also be used as a catchall message when something + * you wanted to do doesn't work, but you don't know + * why. + * EX_SOFTWARE -- An internal software error has been detected. + * This should be limited to non-operating system related + * errors as possible. + * EX_OSERR -- An operating system error has been detected. + * This is intended to be used for such things as "cannot + * fork", "cannot create pipe", or the like. It includes + * things like getuid returning a user that does not + * exist in the passwd file. + * EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp, + * etc.) does not exist, cannot be opened, or has some + * sort of error (e.g., syntax error). + * EX_CANTCREAT -- A (user specified) output file cannot be + * created. + * EX_IOERR -- An error occurred while doing I/O on some file. + * EX_TEMPFAIL -- temporary failure, indicating something that + * is not really an error. In sendmail, this means + * that a mailer (e.g.) could not create a connection, + * and the request should be reattempted later. + * EX_PROTOCOL -- the remote system returned something that + * was "not possible" during a protocol exchange. + * EX_NOPERM -- You did not have sufficient permission to + * perform the operation. This is not intended for + * file system problems, which should use NOINPUT or + * CANTCREAT, but rather for higher level permissions. + */ + +#define EX_OK 0 /* successful termination */ + +#define EX__BASE 64 /* base value for error messages */ + +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ + +#define EX__MAX 78 /* maximum listed value */ + +#endif /* !_SYSEXITS_H_ */ diff --git a/contrib/libs/sasl/include/gai.h b/contrib/libs/sasl/include/gai.h new file mode 100644 index 0000000000..59a38988ea --- /dev/null +++ b/contrib/libs/sasl/include/gai.h @@ -0,0 +1,108 @@ +/* + * Mar 8, 2000 by Hajimu UMEMOTO <ume@mahoroba.org> + * + * This module is besed on ssh-1.2.27-IPv6-1.5 written by + * KIKUCHI Takahiro <kick@kyoto.wide.ad.jp> + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * fake library for ssh + * + * This file is included in getaddrinfo.c and getnameinfo.c. + * See getaddrinfo.c and getnameinfo.c. + */ + +#ifndef _GAI_H_ +#define _GAI_H_ + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#ifndef NI_MAXSERV +#define NI_MAXSERV 32 +#endif + +/* for old netdb.h */ +#ifndef EAI_NODATA +#define EAI_NODATA 1 +#define EAI_MEMORY 2 +#define EAI_FAMILY 5 /* ai_family not supported */ +#define EAI_SERVICE 9 /* servname not supported for ai_socktype */ +#endif + +/* dummy value for old netdb.h */ +#ifndef AI_PASSIVE +#define AI_PASSIVE 1 +#define AI_CANONNAME 2 +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HAVE_GETNAMEINFO +int getnameinfo(const struct sockaddr *, socklen_t, char *, + size_t, char *, size_t, int); +#endif + +#ifndef HAVE_GETADDRINFO +int getaddrinfo(const char *, const char *, + const struct addrinfo *, struct addrinfo **); +void freeaddrinfo(struct addrinfo *); +char *gai_strerror(int); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/libs/sasl/include/hmac-md5.h b/contrib/libs/sasl/include/hmac-md5.h new file mode 100644 index 0000000000..ff81a9d871 --- /dev/null +++ b/contrib/libs/sasl/include/hmac-md5.h @@ -0,0 +1,59 @@ +/* hmac-md5.h -- HMAC_MD5 functions + */ + +#ifndef HMAC_MD5_H +#define HMAC_MD5_H 1 + +#define HMAC_MD5_SIZE 16 + +/* intermediate MD5 context */ +typedef struct HMAC_MD5_CTX_s { + MD5_CTX ictx, octx; +} HMAC_MD5_CTX; + +/* intermediate HMAC state + * values stored in network byte order (Big Endian) + */ +typedef struct HMAC_MD5_STATE_s { + SASL_UINT4 istate[4]; + SASL_UINT4 ostate[4]; +} HMAC_MD5_STATE; + +#ifdef __cplusplus +extern "C" { +#endif + +/* One step hmac computation + * + * digest may be same as text or key + */ +void _sasl_hmac_md5(const unsigned char *text, int text_len, + const unsigned char *key, int key_len, + unsigned char digest[HMAC_MD5_SIZE]); + +/* create context from key + */ +void _sasl_hmac_md5_init(HMAC_MD5_CTX *hmac, + const unsigned char *key, int key_len); + +/* precalculate intermediate state from key + */ +void _sasl_hmac_md5_precalc(HMAC_MD5_STATE *hmac, + const unsigned char *key, int key_len); + +/* initialize context from intermediate state + */ +void _sasl_hmac_md5_import(HMAC_MD5_CTX *hmac, HMAC_MD5_STATE *state); + +#define _sasl_hmac_md5_update(hmac, text, text_len) _sasl_MD5Update(&(hmac)->ictx, (text), (text_len)) + +/* finish hmac from intermediate result. Intermediate result is zeroed. + */ +void _sasl_hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], + HMAC_MD5_CTX *hmac); + +#ifdef __cplusplus +} +#endif + +#endif /* HMAC_MD5_H */ diff --git a/contrib/libs/sasl/include/md5.h b/contrib/libs/sasl/include/md5.h new file mode 100644 index 0000000000..3571930158 --- /dev/null +++ b/contrib/libs/sasl/include/md5.h @@ -0,0 +1,42 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + SASL_UINT4 state[4]; /* state (ABCD) */ + SASL_UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +#ifdef __cplusplus +extern "C" { +#endif + +void _sasl_MD5Init (MD5_CTX *); +void _sasl_MD5Update (MD5_CTX *, const unsigned char *, unsigned int); +void _sasl_MD5Final (unsigned char [16], MD5_CTX *); + +#ifdef __cplusplus +} +#endif diff --git a/contrib/libs/sasl/include/md5global.h b/contrib/libs/sasl/include/md5global.h new file mode 100644 index 0000000000..034d9fa2d4 --- /dev/null +++ b/contrib/libs/sasl/include/md5global.h @@ -0,0 +1,38 @@ +/* GLOBAL.H - RSAREF types and constants + */ +#ifndef MD5GLOBAL_H +#define MD5GLOBAL_H + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. +The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +typedef signed char SASL_INT1; /* 8 bits */ +typedef short SASL_INT2; /* 16 bits */ +typedef int SASL_INT4; /* 32 bits */ +typedef long SASL_INT8; /* 64 bits */ +typedef unsigned char SASL_UINT1; /* 8 bits */ +typedef unsigned short SASL_UINT2; /* 16 bits */ +typedef unsigned int SASL_UINT4; /* 32 bits */ +typedef unsigned long SASL_UINT8; /* 64 bits */ + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. +If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it +returns an empty list. +*/ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +#endif /* MD5GLOBAL_H */ + diff --git a/contrib/libs/sasl/include/prop.h b/contrib/libs/sasl/include/prop.h new file mode 100644 index 0000000000..bf091d67d8 --- /dev/null +++ b/contrib/libs/sasl/include/prop.h @@ -0,0 +1,170 @@ +/* prop.h -- property request/response management routines + * + * Author: Chris Newman + * Removal of implementation-specific details by: Rob Siemborski + * + * This is intended to be used to create a list of properties to request, + * and _then_ request values for all properties. Any change to the request + * list will discard any existing values. This assumption allows a very + * efficient and simple memory model. This was designed for SASL API auxiliary + * property support, but would be fine for other contexts where this property + * model is appropriate. + * + * The "struct propctx" is allocated by prop_new and is a fixed size structure. + * If a prop_init() call were added, it would be reasonable to embed a "struct + * propctx" in another structure. prop_new also allocates a pool of memory + * (in the vbase field) which will be used for an array of "struct propval" + * to list all the requested properties. + * + * Properties may be multi-valued. + */ + +#ifndef PROP_H +#define PROP_H 1 + +/* The following ifdef block is the standard way of creating macros + * which make exporting from a DLL simpler. All files within this DLL + * are compiled with the LIBSASL_EXPORTS symbol defined on the command + * line. this symbol should not be defined on any project that uses + * this DLL. This way any other project whose source files include + * this file see LIBSASL_API functions as being imported from a DLL, + * wheras this DLL sees symbols defined with this macro as being + * exported. */ +/* Under Unix, life is simpler: we just need to mark library functions + * as extern. (Technically, we don't even have to do that.) */ +# define LIBSASL_API extern + +/* Same as above, but used during a variable declaration. */ +# define LIBSASL_VAR extern + +/* the resulting structure for property values + */ +struct propval { + const char *name; /* name of property; NULL = end of list */ + /* same pointer used in request will be used here */ + const char **values; /* list of strings, values == NULL if property not + * found, *values == NULL if property found with + * no values */ + unsigned nvalues; /* total number of value strings */ + unsigned valsize; /* total size in characters of all value strings */ +}; + +/* + * private internal structure + */ +#define PROP_DEFAULT 4 /* default number of propvals to assume */ +struct propctx; + +#ifdef __cplusplus +extern "C" { +#endif + +/* create a property context + * estimate -- an estimate of the storage needed for requests & responses + * 0 will use module default + * returns a new property context on success and NULL on any error + */ +LIBSASL_API struct propctx *prop_new(unsigned estimate); + +/* create new propctx which duplicates the contents of an existing propctx + * returns SASL_OK on success + * possible other return values include: SASL_NOMEM, SASL_BADPARAM + */ +LIBSASL_API int prop_dup(struct propctx *src_ctx, struct propctx **dst_ctx); + +/* Add property names to request + * ctx -- context from prop_new() + * names -- list of property names; must persist until context freed + * or requests cleared (This extends to other contexts that + * are dup'ed from this one, and their children, etc) + * + * NOTE: may clear values from context as side-effect + * returns SASL_OK on success + * possible other return values include: SASL_NOMEM, SASL_BADPARAM + */ +LIBSASL_API int prop_request(struct propctx *ctx, const char **names); + +/* return array of struct propval from the context + * return value persists until next call to + * prop_request, prop_clear or prop_dispose on context + * + * returns NULL on error + */ +LIBSASL_API const struct propval *prop_get(struct propctx *ctx); + +/* Fill in an array of struct propval based on a list of property names + * return value persists until next call to + * prop_request, prop_clear or prop_dispose on context + * returns number of matching properties which were found (values != NULL) + * if a name requested here was never requested by a prop_request, then + * the name field of the associated vals entry will be set to NULL + * + * The vals array MUST be atleast as long as the names array. + * + * returns # of matching properties on success + * possible other return values include: SASL_BADPARAM + */ +LIBSASL_API int prop_getnames(struct propctx *ctx, const char **names, + struct propval *vals); + +/* clear values and optionally requests from property context + * ctx -- property context + * requests -- 0 = don't clear requests, 1 = clear requests + */ +LIBSASL_API void prop_clear(struct propctx *ctx, int requests); + +/* erase the value of a property + */ +LIBSASL_API void prop_erase(struct propctx *ctx, const char *name); + +/* dispose of property context + * ctx -- is disposed and set to NULL; noop if ctx or *ctx is NULL + */ +LIBSASL_API void prop_dispose(struct propctx **ctx); + + +/****fetcher interfaces****/ + +/* format the requested property names into a string + * ctx -- context from prop_new()/prop_request() + * sep -- separator between property names (unused if none requested) + * seplen -- length of separator, if < 0 then strlen(sep) will be used + * outbuf -- output buffer + * outmax -- maximum length of output buffer including NUL terminator + * outlen -- set to length of output string excluding NUL terminator + * returns SASL_OK on success + * returns SASL_BADPARAM or amount of additional space needed on failure + */ +LIBSASL_API int prop_format(struct propctx *ctx, const char *sep, int seplen, + char *outbuf, unsigned outmax, unsigned *outlen); + +/* add a property value to the context + * ctx -- context from prop_new()/prop_request() + * name -- name of property to which value will be added + * if NULL, add to the same name as previous prop_set/setvals call + * value -- a value for the property; will be copied into context + * if NULL, remove existing values + * vallen -- length of value, if <= 0 then strlen(value) will be used + * returns SASL_OK on success + * possible error return values include: SASL_BADPARAM, SASL_NOMEM + */ +LIBSASL_API int prop_set(struct propctx *ctx, const char *name, + const char *value, int vallen); + +/* set the values for a property + * ctx -- context from prop_new()/prop_request() + * name -- name of property to which value will be added + * if NULL, add to the same name as previous prop_set/setvals call + * values -- array of values, ending in NULL. Each value is a NUL terminated + * string + * returns SASL_OK on success + * possible error return values include: SASL_BADPARAM, SASL_NOMEM + */ +LIBSASL_API int prop_setvals(struct propctx *ctx, const char *name, + const char **values); + +#ifdef __cplusplus +} +#endif + +#endif /* PROP_H */ diff --git a/contrib/libs/sasl/include/sasl.h b/contrib/libs/sasl/include/sasl.h new file mode 100644 index 0000000000..dc04ca4714 --- /dev/null +++ b/contrib/libs/sasl/include/sasl.h @@ -0,0 +1,1332 @@ +/* This is a proposed C API for support of SASL + * + *********************************IMPORTANT******************************* + * send email to chris.newman@innosoft.com and cyrus-bugs@andrew.cmu.edu * + * if you need to add new error codes, callback types, property values, * + * etc. It is important to keep the multiple implementations of this * + * API from diverging. * + *********************************IMPORTANT******************************* + * + * Basic Type Summary: + * sasl_conn_t Context for a SASL connection negotiation + * sasl_ssf_t Security layer Strength Factor + * sasl_callback_t A typed client/server callback function and context + * sasl_interact_t A client interaction descriptor + * sasl_secret_t A client password + * sasl_rand_t Random data context structure + * sasl_security_properties_t An application's required security level + * + * Callbacks: + * sasl_getopt_t client/server: Get an option value + * sasl_logmsg_t client/server: Log message handler + * sasl_getsimple_t client: Get user/language list + * sasl_getsecret_t client: Get authentication secret + * sasl_chalprompt_t client: Display challenge and prompt for response + * + * Server only Callbacks: + * sasl_authorize_t user authorization policy callback + * sasl_getconfpath_t get path to search for config file + * sasl_server_userdb_checkpass check password and auxprops in userdb + * sasl_server_userdb_setpass set password in userdb + * sasl_server_canon_user canonicalize username routine + * + * Client/Server Function Summary: + * sasl_done Release all SASL global state + * sasl_dispose Connection done: Dispose of sasl_conn_t + * sasl_getprop Get property (e.g., user name, security layer info) + * sasl_setprop Set property (e.g., external ssf) + * sasl_errdetail Generate string from last error on connection + * sasl_errstring Translate sasl error code to a string + * sasl_encode Encode data to send using security layer + * sasl_decode Decode data received using security layer + * + * Utility functions: + * sasl_encode64 Encode data to send using MIME base64 encoding + * sasl_decode64 Decode data received using MIME base64 encoding + * sasl_erasebuffer Erase a buffer + * + * Client Function Summary: + * sasl_client_init Load and initialize client plug-ins (call once) + * sasl_client_new Initialize client connection context: sasl_conn_t + * sasl_client_start Select mechanism for connection + * sasl_client_step Perform one authentication step + * + * Server Function Summary + * sasl_server_init Load and initialize server plug-ins (call once) + * sasl_server_new Initialize server connection context: sasl_conn_t + * sasl_listmech Create list of available mechanisms + * sasl_server_start Begin an authentication exchange + * sasl_server_step Perform one authentication exchange step + * sasl_checkpass Check a plaintext passphrase + * sasl_checkapop Check an APOP challenge/response (uses pseudo "APOP" + * mechanism similar to CRAM-MD5 mechanism; optional) + * sasl_user_exists Check if user exists + * sasl_setpass Change a password or add a user entry + * sasl_auxprop_request Request auxiliary properties + * sasl_auxprop_getctx Get auxiliary property context for connection + * sasl_auxprop_store Store a set of auxiliary properties + * + * Basic client model: + * 1. client calls sasl_client_init() at startup to load plug-ins + * 2. when connection formed, call sasl_client_new() + * 3. once list of supported mechanisms received from server, client + * calls sasl_client_start(). goto 4a + * 4. client calls sasl_client_step() + * [4a. If SASL_INTERACT, fill in prompts and goto 4 + * -- doesn't happen if callbacks provided] + * 4b. If SASL error, goto 7 or 3 + * 4c. If SASL_OK, continue or goto 6 if last server response was success + * 5. send message to server, wait for response + * 5a. On data or success with server response, goto 4 + * 5b. On failure goto 7 or 3 + * 5c. On success with no server response continue + * 6. continue with application protocol until connection closes + * call sasl_getprop/sasl_encode/sasl_decode() if using security layer + * 7. call sasl_dispose(), may return to step 2 + * 8. call sasl_done() when program terminates + * + * Basic Server model: + * 1. call sasl_server_init() at startup to load plug-ins + * 2. On connection, call sasl_server_new() + * 3. call sasl_listmech() and send list to client] + * 4. after client AUTH command, call sasl_server_start(), goto 5a + * 5. call sasl_server_step() + * 5a. If SASL_CONTINUE, output to client, wait response, repeat 5 + * 5b. If SASL error, then goto 7 + * 5c. If SASL_OK, move on + * 6. continue with application protocol until connection closes + * call sasl_getprop to get username + * call sasl_getprop/sasl_encode/sasl_decode() if using security layer + * 7. call sasl_dispose(), may return to step 2 + * 8. call sasl_done() when program terminates + * + ************************************************* + * IMPORTANT NOTE: server realms / username syntax + * + * If a user name contains a "@", then the rightmost "@" in the user name + * separates the account name from the realm in which this account is + * located. A single server may support multiple realms. If the + * server knows the realm at connection creation time (e.g., a server + * with multiple IP addresses tightly binds one address to a specific + * realm) then that realm must be passed in the user_realm field of + * the sasl_server_new call. If user_realm is non-empty and an + * unqualified user name is supplied, then the canon_user facility is + * expected to append "@" and user_realm to the user name. The canon_user + * facility may treat other characters such as "%" as equivalent to "@". + * + * If the server forbids the use of "@" in user names for other + * purposes, this simplifies security validation. + */ + +#ifndef SASL_H +#define SASL_H 1 + +#include <stddef.h> /* For size_t */ + +/* Keep in sync with win32/common.mak */ +#define SASL_VERSION_MAJOR 2 +#define SASL_VERSION_MINOR 1 +#define SASL_VERSION_STEP 28 + +/* A convenience macro: same as was defined in the OpenLDAP LDAPDB */ +#define SASL_VERSION_FULL ((SASL_VERSION_MAJOR << 16) |\ + (SASL_VERSION_MINOR << 8) | SASL_VERSION_STEP) + +#include "prop.h" + +/************* + * Basic API * + *************/ + +/* SASL result codes: */ +#define SASL_CONTINUE 1 /* another step is needed in authentication */ +#define SASL_OK 0 /* successful result */ +#define SASL_FAIL -1 /* generic failure */ +#define SASL_NOMEM -2 /* memory shortage failure */ +#define SASL_BUFOVER -3 /* overflowed buffer */ +#define SASL_NOMECH -4 /* mechanism not supported */ +#define SASL_BADPROT -5 /* bad protocol / cancel */ +#define SASL_NOTDONE -6 /* can't request info until later in exchange */ +#define SASL_BADPARAM -7 /* invalid parameter supplied */ +#define SASL_TRYAGAIN -8 /* transient failure (e.g., weak key) */ +#define SASL_BADMAC -9 /* integrity check failed */ +#define SASL_NOTINIT -12 /* SASL library not initialized */ + /* -- client only codes -- */ +#define SASL_INTERACT 2 /* needs user interaction */ +#define SASL_BADSERV -10 /* server failed mutual authentication step */ +#define SASL_WRONGMECH -11 /* mechanism doesn't support requested feature */ + /* -- server only codes -- */ +#define SASL_BADAUTH -13 /* authentication failure */ +#define SASL_NOAUTHZ -14 /* authorization failure */ +#define SASL_TOOWEAK -15 /* mechanism too weak for this user */ +#define SASL_ENCRYPT -16 /* encryption needed to use mechanism */ +#define SASL_TRANS -17 /* One time use of a plaintext password will + enable requested mechanism for user */ +#define SASL_EXPIRED -18 /* passphrase expired, has to be reset */ +#define SASL_DISABLED -19 /* account disabled */ +#define SASL_NOUSER -20 /* user not found */ +#define SASL_BADVERS -23 /* version mismatch with plug-in */ +#define SASL_UNAVAIL -24 /* remote authentication server unavailable */ +#define SASL_NOVERIFY -26 /* user exists, but no verifier for user */ + /* -- codes for password setting -- */ +#define SASL_PWLOCK -21 /* passphrase locked */ +#define SASL_NOCHANGE -22 /* requested change was not needed */ +#define SASL_WEAKPASS -27 /* passphrase is too weak for security policy */ +#define SASL_NOUSERPASS -28 /* user supplied passwords not permitted */ +#define SASL_NEED_OLD_PASSWD -29 /* sasl_setpass needs old password in order + to perform password change */ +#define SASL_CONSTRAINT_VIOLAT -30 /* a property can't be stored, + because of some constrains/policy violation */ + +#define SASL_BADBINDING -32 /* channel binding failure */ +#define SASL_CONFIGERR -100 /* error when parsing configuration file */ + +/* max size of a sasl mechanism name */ +#define SASL_MECHNAMEMAX 20 + +#ifdef _WIN32 +/* Define to have the same layout as a WSABUF */ +#ifndef STRUCT_IOVEC_DEFINED +#define STRUCT_IOVEC_DEFINED 1 +struct iovec { + long iov_len; + char *iov_base; +}; +#endif +#else +struct iovec; /* Defined in OS headers */ +#endif + + +/* per-connection SASL negotiation state for client or server + */ +typedef struct sasl_conn sasl_conn_t; + +/* Plain text password structure. + * len is the length of the password, data is the text. + */ +typedef struct sasl_secret { + unsigned long len; + unsigned char data[1]; /* variable sized */ +} sasl_secret_t; + +/* random data context structure + */ +typedef struct sasl_rand_s sasl_rand_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************** + * Configure Basic Services * + ****************************/ + +/* the following functions are used to adjust how allocation and mutexes work + * they must be called before all other SASL functions: + */ + +#include <sys/types.h> + +/* memory allocation functions which may optionally be replaced: + */ +typedef void *sasl_malloc_t(size_t); +typedef void *sasl_calloc_t(size_t, size_t); +typedef void *sasl_realloc_t(void *, size_t); +typedef void sasl_free_t(void *); + +LIBSASL_API void sasl_set_alloc(sasl_malloc_t *, + sasl_calloc_t *, + sasl_realloc_t *, + sasl_free_t *); + +/* mutex functions which may optionally be replaced: + * sasl_mutex_alloc allocates a mutex structure + * sasl_mutex_lock blocks until mutex locked + * returns -1 on deadlock or parameter error + * returns 0 on success + * sasl_mutex_unlock unlocks mutex if it's locked + * returns -1 if not locked or parameter error + * returns 0 on success + * sasl_mutex_free frees a mutex structure + */ +typedef void *sasl_mutex_alloc_t(void); +typedef int sasl_mutex_lock_t(void *mutex); +typedef int sasl_mutex_unlock_t(void *mutex); +typedef void sasl_mutex_free_t(void *mutex); +LIBSASL_API void sasl_set_mutex(sasl_mutex_alloc_t *, sasl_mutex_lock_t *, + sasl_mutex_unlock_t *, sasl_mutex_free_t *); + +/***************************** + * Security preference types * + *****************************/ + +/* security layer strength factor -- an unsigned integer usable by the caller + * to specify approximate security layer strength desired. Roughly + * correlated to effective key length for encryption. + * 0 = no protection + * 1 = integrity protection only + * 40 = 40-bit DES or 40-bit RC2/RC4 + * 56 = DES + * 112 = triple-DES + * 128 = 128-bit RC2/RC4/BLOWFISH + * 256 = baseline AES + */ +typedef unsigned sasl_ssf_t; + +/* usage flags provided to sasl_server_new and sasl_client_new: + */ +#define SASL_SUCCESS_DATA 0x0004 /* server supports data on success */ +#define SASL_NEED_PROXY 0x0008 /* require a mech that allows proxying */ +#define SASL_NEED_HTTP 0x0010 /* require a mech that can do HTTP auth */ + +/*************************** + * Security Property Types * + ***************************/ + +/* Structure specifying the client or server's security policy + * and optional additional properties. + */ + +/* These are the various security flags apps can specify. */ +/* NOPLAINTEXT -- don't permit mechanisms susceptible to simple + * passive attack (e.g., PLAIN, LOGIN) + * NOACTIVE -- protection from active (non-dictionary) attacks + * during authentication exchange. + * Authenticates server. + * NODICTIONARY -- don't permit mechanisms susceptible to passive + * dictionary attack + * FORWARD_SECRECY -- require forward secrecy between sessions + * (breaking one won't help break next) + * NOANONYMOUS -- don't permit mechanisms that allow anonymous login + * PASS_CREDENTIALS -- require mechanisms which pass client + * credentials, and allow mechanisms which can pass + * credentials to do so + * MUTUAL_AUTH -- require mechanisms which provide mutual + * authentication + */ +#define SASL_SEC_NOPLAINTEXT 0x0001 +#define SASL_SEC_NOACTIVE 0x0002 +#define SASL_SEC_NODICTIONARY 0x0004 +#define SASL_SEC_FORWARD_SECRECY 0x0008 +#define SASL_SEC_NOANONYMOUS 0x0010 +#define SASL_SEC_PASS_CREDENTIALS 0x0020 +#define SASL_SEC_MUTUAL_AUTH 0x0040 +#define SASL_SEC_MAXIMUM 0xFFFF + +/* This is used when adding hash size to the security_flags field */ +/* NB: hash size is in bits */ +#define SASL_SET_HASH_STRENGTH_BITS(x) (((x) / 8) << 16) + +/* NB: This value is in bytes */ +#define SASL_GET_HASH_STRENGTH(x) ((x) >> 16) + +typedef struct sasl_security_properties +{ + /* security strength factor + * min_ssf = minimum acceptable final level + * max_ssf = maximum acceptable final level + */ + sasl_ssf_t min_ssf; + sasl_ssf_t max_ssf; + + /* Maximum security layer receive buffer size. + * 0=security layer not supported + */ + unsigned maxbufsize; + + /* bitfield for attacks to protect against */ + unsigned security_flags; + + /* NULL terminated array of additional property names, values */ + const char **property_names; + const char **property_values; +} sasl_security_properties_t; + +/****************** + * Callback types * + ******************/ + +/* + * Extensible type for a client/server callbacks + * id -- identifies callback type + * proc -- procedure call arguments vary based on id + * context -- context passed to procedure + */ +/* Note that any memory that is allocated by the callback needs to be + * freed by the application, be it via function call or interaction. + * + * It may be freed after sasl_*_step returns SASL_OK. if the mechanism + * requires this information to persist (for a security layer, for example) + * it must maintain a private copy. + */ +typedef struct sasl_callback { + /* Identifies the type of the callback function. + * Mechanisms must ignore callbacks with id's they don't recognize. + */ + unsigned long id; + int (*proc)(void); /* Callback function. Types of arguments vary by 'id' */ + void *context; +} sasl_callback_t; + +/* callback ids & functions: + */ +#define SASL_CB_LIST_END 0 /* end of list */ + +/* option reading callback -- this allows a SASL configuration to be + * encapsulated in the caller's configuration system. Some implementations + * may use default config file(s) if this is omitted. Configuration items + * may be plugin-specific and are arbitrary strings. + * + * inputs: + * context -- option context from callback record + * plugin_name -- name of plugin (NULL = general SASL option) + * option -- name of option + * output: + * result -- set to result which persists until next getopt in + * same thread, unchanged if option not found + * len -- length of result (may be NULL) + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_getopt_t(void *context, const char *plugin_name, + const char *option, + const char **result, unsigned *len); +#define SASL_CB_GETOPT 1 + +/* Logging levels for use with the logging callback function. */ +#define SASL_LOG_NONE 0 /* don't log anything */ +#define SASL_LOG_ERR 1 /* log unusual errors (default) */ +#define SASL_LOG_FAIL 2 /* log all authentication failures */ +#define SASL_LOG_WARN 3 /* log non-fatal warnings */ +#define SASL_LOG_NOTE 4 /* more verbose than LOG_WARN */ +#define SASL_LOG_DEBUG 5 /* more verbose than LOG_NOTE */ +#define SASL_LOG_TRACE 6 /* traces of internal protocols */ +#define SASL_LOG_PASS 7 /* traces of internal protocols, including + * passwords */ + +/* logging callback -- this allows plugins and the middleware to + * log operations they perform. + * inputs: + * context -- logging context from the callback record + * level -- logging level; see above + * message -- message to log + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_log_t(void *context, + int level, + const char *message); +#define SASL_CB_LOG 2 + +/* getpath callback -- this allows applications to specify the + * colon-separated path to search for plugins (by default, + * taken from an implementation-specific location). + * inputs: + * context -- getpath context from the callback record + * outputs: + * path -- colon seperated path + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_getpath_t(void *context, + const char **path); + +#define SASL_CB_GETPATH 3 + +/* verify file callback -- this allows applications to check if they + * want SASL to use files, file by file. This is intended to allow + * applications to sanity check the environment to make sure plugins + * or the configuration file can't be written to, etc. + * inputs: + * context -- verifypath context from the callback record + * file -- full path to file to verify + * type -- type of file to verify (see below) + + * returns: + * SASL_OK -- no error (file can safely be used) + * SASL_CONTINUE -- continue WITHOUT using this file + * SASL_FAIL -- error + */ + +/* these are the types of files libsasl will ask about */ +typedef enum { + SASL_VRFY_PLUGIN=0, /* a DLL/shared library plug-in */ + SASL_VRFY_CONF=1, /* a configuration file */ + SASL_VRFY_PASSWD=2, /* a password storage file/db */ + SASL_VRFY_OTHER=3 /* some other file */ +} sasl_verify_type_t; + +typedef int sasl_verifyfile_t(void *context, + const char *file, sasl_verify_type_t type); +#define SASL_CB_VERIFYFILE 4 + +/* getconfpath callback -- this allows applications to specify the + * colon-separated path to search for config files (by default, + * taken from the SASL_CONF_PATH environment variable). + * inputs: + * context -- getconfpath context from the callback record + * outputs: + * path -- colon seperated path (allocated on the heap; the + * library will free it using the sasl_free_t * + * passed to sasl_set_callback, or the standard free() + * library call). + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_getconfpath_t(void *context, + char **path); + +#define SASL_CB_GETCONFPATH 5 + +/* client/user interaction callbacks: + */ +/* Simple prompt -- result must persist until next call to getsimple on + * same connection or until connection context is disposed + * inputs: + * context -- context from callback structure + * id -- callback id + * outputs: + * result -- set to NUL terminated string + * NULL = user cancel + * len -- length of result + * returns SASL_OK + */ +typedef int sasl_getsimple_t(void *context, int id, + const char **result, unsigned *len); +#define SASL_CB_USER 0x4001 /* client user identity to login as */ +#define SASL_CB_AUTHNAME 0x4002 /* client authentication name */ +#define SASL_CB_LANGUAGE 0x4003 /* comma separated list of RFC 1766 + * language codes in order of preference + * to be used to localize client prompts + * or server error codes */ +#define SASL_CB_CNONCE 0x4007 /* caller supplies client-nonce + * primarily for testing purposes */ + +/* get a sasl_secret_t (plaintext password with length) + * inputs: + * conn -- connection context + * context -- context from callback structure + * id -- callback id + * outputs: + * psecret -- set to NULL to cancel + * set to password structure which must persist until + * next call to getsecret in same connection, but middleware + * will erase password data when it's done with it. + * returns SASL_OK + */ +typedef int sasl_getsecret_t(sasl_conn_t *conn, void *context, int id, + sasl_secret_t **psecret); +#define SASL_CB_PASS 0x4004 /* client passphrase-based secret */ + + +/* prompt for input in response to a challenge. + * input: + * context -- context from callback structure + * id -- callback id + * challenge -- server challenge + * output: + * result -- NUL terminated result, NULL = user cancel + * len -- length of result + * returns SASL_OK + */ +typedef int sasl_chalprompt_t(void *context, int id, + const char *challenge, + const char *prompt, const char *defresult, + const char **result, unsigned *len); +#define SASL_CB_ECHOPROMPT 0x4005 /* challenge and client enterred result */ +#define SASL_CB_NOECHOPROMPT 0x4006 /* challenge and client enterred result */ + +/* prompt (or autoselect) the realm to do authentication in. + * may get a list of valid realms. + * input: + * context -- context from callback structure + * id -- callback id + * availrealms -- available realms; string list; NULL terminated + * list may be empty. + * output: + * result -- NUL terminated realm; NULL is equivalent to "" + * returns SASL_OK + * result must persist until the next callback + */ +typedef int sasl_getrealm_t(void *context, int id, + const char **availrealms, + const char **result); +#define SASL_CB_GETREALM (0x4008) /* realm to attempt authentication in */ + +/* server callbacks: + */ + +/* improved callback to verify authorization; + * canonicalization now handled elsewhere + * conn -- connection context + * requested_user -- the identity/username to authorize (NUL terminated) + * rlen -- length of requested_user + * auth_identity -- the identity associated with the secret (NUL terminated) + * alen -- length of auth_identity + * default_realm -- default user realm, as passed to sasl_server_new if + * urlen -- length of default realm + * propctx -- auxiliary properties + * returns SASL_OK on success, + * SASL_NOAUTHZ or other SASL response on failure + */ +typedef int sasl_authorize_t(sasl_conn_t *conn, + void *context, + const char *requested_user, unsigned rlen, + const char *auth_identity, unsigned alen, + const char *def_realm, unsigned urlen, + struct propctx *propctx); +#define SASL_CB_PROXY_POLICY 0x8001 + +/* functions for "userdb" based plugins to call to get/set passwords. + * the location for the passwords is determined by the caller or middleware. + * plug-ins may get passwords from other locations. + */ + +/* callback to verify a plaintext password against the caller-supplied + * user database. This is necessary to allow additional <method>s for + * encoding of the userPassword property. + * user -- NUL terminated user name with user@realm syntax + * pass -- password to check (may not be NUL terminated) + * passlen -- length of password to check + * propctx -- auxiliary properties for user + */ +typedef int sasl_server_userdb_checkpass_t(sasl_conn_t *conn, + void *context, + const char *user, + const char *pass, + unsigned passlen, + struct propctx *propctx); +#define SASL_CB_SERVER_USERDB_CHECKPASS (0x8005) + +/* callback to store/change a plaintext password in the user database + * user -- NUL terminated user name with user@realm syntax + * pass -- password to store (may not be NUL terminated) + * passlen -- length of password to store + * propctx -- auxiliary properties (not stored) + * flags -- see SASL_SET_* flags below (SASL_SET_CREATE optional) + */ +typedef int sasl_server_userdb_setpass_t(sasl_conn_t *conn, + void *context, + const char *user, + const char *pass, + unsigned passlen, + struct propctx *propctx, + unsigned flags); +#define SASL_CB_SERVER_USERDB_SETPASS (0x8006) + +/* callback for a server-supplied user canonicalization function. + * + * This function is called directly after the mechanism has the + * authentication and authorization IDs. It is called before any + * User Canonicalization plugin is called. It has the responsibility + * of copying its output into the provided output buffers. + * + * in, inlen -- user name to canonicalize, may not be NUL terminated + * may be same buffer as out + * flags -- not currently used, supplied by auth mechanism + * user_realm -- the user realm (may be NULL in case of client) + * out -- buffer to copy user name + * out_max -- max length of user name + * out_len -- set to length of user name + * + * returns + * SASL_OK on success + * SASL_BADPROT username contains invalid character + */ + +/* User Canonicalization Function Flags */ + +#define SASL_CU_NONE 0x00 /* Not a valid flag to pass */ +/* One of the following two is required */ +#define SASL_CU_AUTHID 0x01 +#define SASL_CU_AUTHZID 0x02 + +/* Combine the following with SASL_CU_AUTHID, if you don't want + to fail if auxprop returned SASL_NOUSER/SASL_NOMECH. */ +#define SASL_CU_EXTERNALLY_VERIFIED 0x04 + +#define SASL_CU_OVERRIDE 0x08 /* mapped to SASL_AUXPROP_OVERRIDE */ + +/* The following CU flags are passed "as is" down to auxprop lookup */ +#define SASL_CU_ASIS_MASK 0xFFF0 +/* NOTE: Keep in sync with SASL_AUXPROP_<XXX> flags */ +#define SASL_CU_VERIFY_AGAINST_HASH 0x10 + + +typedef int sasl_canon_user_t(sasl_conn_t *conn, + void *context, + const char *in, unsigned inlen, + unsigned flags, + const char *user_realm, + char *out, + unsigned out_max, unsigned *out_len); + +#define SASL_CB_CANON_USER (0x8007) + +/********************************** + * Common Client/server functions * + **********************************/ + +/* Types of paths to set (see sasl_set_path below). */ +#define SASL_PATH_TYPE_PLUGIN 0 +#define SASL_PATH_TYPE_CONFIG 1 + +/* a simpler way to set plugin path or configuration file path + * without the need to set sasl_getpath_t callback. + * + * This function can be called before sasl_server_init/sasl_client_init. + */ +LIBSASL_API int sasl_set_path (int path_type, char * path); + +/* get sasl library version information + * implementation is a vendor-defined string + * version is a vender-defined representation of the version #. + * + * This function is being deprecated in favor of sasl_version_info. */ +LIBSASL_API void sasl_version(const char **implementation, + int *version); + +/* Extended version of sasl_version(). + * + * This function is to be used + * for library version display and logging + * for bug workarounds in old library versions + * + * The sasl_version_info is not to be used for API feature detection. + * + * All parameters are optional. If NULL is specified, the value is not returned. + */ +LIBSASL_API void sasl_version_info (const char **implementation, + const char **version_string, + int *version_major, + int *version_minor, + int *version_step, + int *version_patch); + +/* dispose of all SASL plugins. Connection + * states have to be disposed of before calling this. + * + * This function is DEPRECATED in favour of sasl_server_done/ + * sasl_client_done. + */ +LIBSASL_API void sasl_done(void); + +/* dispose of all SASL plugins. Connection + * states have to be disposed of before calling this. + * This function should be called instead of sasl_done(), + whenever possible. + */ +LIBSASL_API int sasl_server_done(void); + +/* dispose of all SASL plugins. Connection + * states have to be disposed of before calling this. + * This function should be called instead of sasl_done(), + whenever possible. + */ +LIBSASL_API int sasl_client_done(void); + +/* dispose connection state, sets it to NULL + * checks for pointer to NULL + */ +LIBSASL_API void sasl_dispose(sasl_conn_t **pconn); + +/* translate an error number into a string + * input: + * saslerr -- the error number + * langlist -- comma separated list of RFC 1766 languages (may be NULL) + * results: + * outlang -- the language actually used (may be NULL if don't care) + * returns: + * the error message in UTF-8 (only the US-ASCII subset if langlist is NULL) + */ +LIBSASL_API const char *sasl_errstring(int saslerr, + const char *langlist, + const char **outlang); + +/* get detail about the last error that occurred on a connection + * text is sanitized so it's suitable to send over the wire + * (e.g., no distinction between SASL_BADAUTH and SASL_NOUSER) + * input: + * conn -- mandatory connection context + * returns: + * the error message in UTF-8 (only the US-ASCII subset permitted if no + * SASL_CB_LANGUAGE callback is present) + */ +LIBSASL_API const char *sasl_errdetail(sasl_conn_t *conn); + +/* set the error string which will be returned by sasl_errdetail() using + * syslog()-style formatting (e.g. printf-style with %m as most recent + * errno error) + * + * primarily for use by server callbacks such as the sasl_authorize_t + * callback and internally to plug-ins + * + * This will also trigger a call to the SASL logging callback (if any) + * with a level of SASL_LOG_FAIL unless the SASL_NOLOG flag is set. + * + * Messages should be sensitive to the current language setting. If there + * is no SASL_CB_LANGUAGE callback messages MUST be US-ASCII otherwise UTF-8 + * is used and use of RFC 2482 for mixed-language text is encouraged. + * + * if conn is NULL, function does nothing + */ +LIBSASL_API void sasl_seterror(sasl_conn_t *conn, unsigned flags, + const char *fmt, ...); +#define SASL_NOLOG 0x01 + +/* get property from SASL connection state + * propnum -- property number + * pvalue -- pointer to value + * returns: + * SASL_OK -- no error + * SASL_NOTDONE -- property not available yet + * SASL_BADPARAM -- bad property number + */ +LIBSASL_API int sasl_getprop(sasl_conn_t *conn, int propnum, + const void **pvalue); +#define SASL_USERNAME 0 /* pointer to NUL terminated user name */ +#define SASL_SSF 1 /* security layer security strength factor, + * if 0, call to sasl_encode, sasl_decode + * unnecessary */ +#define SASL_MAXOUTBUF 2 /* security layer max output buf unsigned */ +#define SASL_DEFUSERREALM 3 /* default realm passed to server_new */ + /* or set with setprop */ +#define SASL_GETOPTCTX 4 /* context for getopt callback */ +#define SASL_CALLBACK 7 /* current callback function list */ +#define SASL_IPLOCALPORT 8 /* iplocalport string passed to server_new */ +#define SASL_IPREMOTEPORT 9 /* ipremoteport string passed to server_new */ + +/* This returns a string which is either empty or has an error message + * from sasl_seterror (e.g., from a plug-in or callback). It differs + * from the result of sasl_errdetail() which also takes into account the + * last return status code. + */ +#define SASL_PLUGERR 10 + +/* a handle to any delegated credentials or NULL if none is present + * is returned by the mechanism. The user will probably need to know + * which mechanism was used to actually known how to make use of them + * currently only implemented for the gssapi mechanism */ +#define SASL_DELEGATEDCREDS 11 + +#define SASL_SERVICE 12 /* service passed to sasl_*_new */ +#define SASL_SERVERFQDN 13 /* serverFQDN passed to sasl_*_new */ +#define SASL_AUTHSOURCE 14 /* name of auth source last used, useful + * for failed authentication tracking */ +#define SASL_MECHNAME 15 /* active mechanism name, if any */ +#define SASL_AUTHUSER 16 /* authentication/admin user */ +#define SASL_APPNAME 17 /* application name (used for logging/ + configuration), same as appname parameter + to sasl_server_init */ + +/* GSS-API credential handle for sasl_client_step() or sasl_server_step(). + * The application is responsible for releasing this credential handle. */ +#define SASL_GSS_CREDS 18 + +/* GSS name (gss_name_t) of the peer, as output by gss_inquire_context() + * or gss_accept_sec_context(). + * On server end this is similar to SASL_USERNAME, but the gss_name_t + * structure can contain additional attributes associated with the peer. + */ +#define SASL_GSS_PEER_NAME 19 + +/* Local GSS name (gss_name_t) as output by gss_inquire_context(). This + * is particularly useful for servers that respond to multiple names. */ +#define SASL_GSS_LOCAL_NAME 20 + +/* Channel binding information. Memory is managed by the caller. */ +typedef struct sasl_channel_binding { + const char *name; + int critical; + unsigned long len; + const unsigned char *data; +} sasl_channel_binding_t; + +#define SASL_CHANNEL_BINDING 21 + +/* HTTP Request (RFC 2616) - ONLY used for HTTP Digest Auth (RFC 2617) */ +typedef struct sasl_http_request { + const char *method; /* HTTP Method */ + const char *uri; /* request-URI */ + const unsigned char *entity; /* entity-body */ + unsigned long elen; /* entity-body length */ + unsigned non_persist; /* Is it a non-persistent connection? */ +} sasl_http_request_t; + +#define SASL_HTTP_REQUEST 22 + +/* set property in SASL connection state + * returns: + * SASL_OK -- value set + * SASL_BADPARAM -- invalid property or value + */ +LIBSASL_API int sasl_setprop(sasl_conn_t *conn, + int propnum, + const void *value); +#define SASL_SSF_EXTERNAL 100 /* external SSF active (sasl_ssf_t *) */ +#define SASL_SEC_PROPS 101 /* sasl_security_properties_t */ +#define SASL_AUTH_EXTERNAL 102 /* external authentication ID (const char *) */ + +/* If the SASL_AUTH_EXTERNAL value is non-NULL, then a special version of the + * EXTERNAL mechanism is enabled (one for server-embedded EXTERNAL mechanisms). + * Otherwise, the EXTERNAL mechanism will be absent unless a plug-in + * including EXTERNAL is present. + */ + +/* do precalculations during an idle period or network round trip + * may pass NULL to precompute for some mechanisms prior to connect + * returns 1 if action taken, 0 if no action taken + */ +LIBSASL_API int sasl_idle(sasl_conn_t *conn); + +/************** + * Client API * + **************/ + +/* list of client interactions with user for caller to fill in + */ +typedef struct sasl_interact { + unsigned long id; /* same as client/user callback ID */ + const char *challenge; /* presented to user (e.g. OTP challenge) */ + const char *prompt; /* presented to user (e.g. "Username: ") */ + const char *defresult; /* default result string */ + const void *result; /* set to point to result */ + unsigned len; /* set to length of result */ +} sasl_interact_t; + +/* initialize the SASL client drivers + * callbacks -- base callbacks for all client connections; + * must include getopt callback + * returns: + * SASL_OK -- Success + * SASL_NOMEM -- Not enough memory + * SASL_BADVERS -- Mechanism version mismatch + * SASL_BADPARAM -- missing getopt callback or error in config file + * SASL_NOMECH -- No mechanisms available + * ... + */ +LIBSASL_API int sasl_client_init(const sasl_callback_t *callbacks); + +/* initialize a client exchange based on the specified mechanism + * service -- registered name of the service using SASL (e.g. "imap") + * serverFQDN -- the fully qualified domain name of the server + * iplocalport -- client IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * ipremoteport -- server IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * prompt_supp -- list of client interactions supported + * may also include sasl_getopt_t context & call + * NULL prompt_supp = user/pass via SASL_INTERACT only + * NULL proc = interaction supported via SASL_INTERACT + * flags -- server usage flags (see above) + * in/out: + * pconn -- connection negotiation structure + * pointer to NULL => allocate new + * + * Returns: + * SASL_OK -- success + * SASL_NOMECH -- no mechanism meets requested properties + * SASL_NOMEM -- not enough memory + */ +LIBSASL_API int sasl_client_new(const char *service, + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *prompt_supp, + unsigned flags, + sasl_conn_t **pconn); + +/* select a mechanism for a connection + * mechlist -- mechanisms server has available (punctuation ignored) + * if NULL, then discard cached info and retry last mech + * output: + * prompt_need -- on SASL_INTERACT, list of prompts needed to continue + * may be NULL if callbacks provided + * clientout -- the initial client response to send to the server + * will be valid until next call to client_start/client_step + * NULL if mech doesn't include initial client challenge + * mech -- set to mechansm name of selected mechanism (may be NULL) + * + * Returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + * SASL_NOMECH -- no mechanism meets requested properties + * SASL_INTERACT -- user interaction needed to fill in prompt_need list + */ +LIBSASL_API int sasl_client_start(sasl_conn_t *conn, + const char *mechlist, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + const char **mech); + +/* do a single authentication step. + * serverin -- the server message received by the client, MUST have a NUL + * sentinel, not counted by serverinlen + * output: + * prompt_need -- on SASL_INTERACT, list of prompts needed to continue + * clientout -- the client response to send to the server + * will be valid until next call to client_start/client_step + * + * returns: + * SASL_OK -- success + * SASL_INTERACT -- user interaction needed to fill in prompt_need list + * SASL_BADPROT -- server protocol incorrect/cancelled + * SASL_BADSERV -- server failed mutual auth + */ +LIBSASL_API int sasl_client_step(sasl_conn_t *conn, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen); + +/************** + * Server API * + **************/ + +/* initialize server drivers, done once per process + * callbacks -- callbacks for all server connections; must include + * getopt callback + * appname -- name of calling application (for lower level logging) + * results: + * state -- server state + * returns: + * SASL_OK -- success + * SASL_BADPARAM -- error in config file + * SASL_NOMEM -- memory failure + * SASL_BADVERS -- Mechanism version mismatch + */ +LIBSASL_API int sasl_server_init(const sasl_callback_t *callbacks, + const char *appname); + +/* IP/port syntax: + * a.b.c.d;p where a-d are 0-255 and p is 0-65535 port number. + * e:f:g:h:i:j:k:l;p where e-l are 0000-ffff lower-case hexidecimal + * e:f:g:h:i:j:a.b.c.d;p alternate syntax for previous + * + * Note that one or more "0" fields in f-k can be replaced with "::" + * Thus: e:f:0000:0000:0000:j:k:l;p + * can be abbreviated: e:f::j:k:l;p + * + * A buffer of size 52 is adequate for the longest format with NUL terminator. + */ + +/* create context for a single SASL connection + * service -- registered name of the service using SASL (e.g. "imap") + * serverFQDN -- Fully qualified domain name of server. NULL means use + * gethostname() or equivalent. + * Useful for multi-homed servers. + * user_realm -- permits multiple user realms on server, NULL = default + * iplocalport -- server IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * ipremoteport -- client IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * callbacks -- callbacks (e.g., authorization, lang, new getopt context) + * flags -- usage flags (see above) + * returns: + * pconn -- new connection context + * + * returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + */ +LIBSASL_API int sasl_server_new(const char *service, + const char *serverFQDN, + const char *user_realm, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *callbacks, + unsigned flags, + sasl_conn_t **pconn); + +/* Return an array of NUL-terminated strings, terminated by a NULL pointer, + * which lists all possible mechanisms that the library can supply + * + * Returns NULL on failure. */ +LIBSASL_API const char ** sasl_global_listmech(void); + +/* This returns a list of mechanisms in a NUL-terminated string + * conn -- the connection to list mechanisms for (either client + * or server) + * user -- restricts mechanisms to those available to that user + * (may be NULL, not used for client case) + * prefix -- appended to beginning of result + * sep -- appended between mechanisms + * suffix -- appended to end of result + * results: + * result -- NUL terminated result which persists until next + * call to sasl_listmech for this sasl_conn_t + * plen -- gets length of result (excluding NUL), may be NULL + * pcount -- gets number of mechanisms, may be NULL + * + * returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + * SASL_NOMECH -- no enabled mechanisms + */ +LIBSASL_API int sasl_listmech(sasl_conn_t *conn, + const char *user, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount); + +/* start a mechanism exchange within a connection context + * mech -- the mechanism name client requested + * clientin -- client initial response (NUL terminated), NULL if empty + * clientinlen -- length of initial response + * serverout -- initial server challenge, NULL if done + * (library handles freeing this string) + * serveroutlen -- length of initial server challenge + * output: + * pconn -- the connection negotiation state on success + * + * Same returns as sasl_server_step() or + * SASL_NOMECH if mechanism not available. + */ +LIBSASL_API int sasl_server_start(sasl_conn_t *conn, + const char *mech, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen); + +/* perform one step of the SASL exchange + * inputlen & input -- client data + * NULL on first step if no optional client step + * outputlen & output -- set to the server data to transmit + * to the client in the next step + * (library handles freeing this) + * + * returns: + * SASL_OK -- exchange is complete. + * SASL_CONTINUE -- indicates another step is necessary. + * SASL_TRANS -- entry for user exists, but not for mechanism + * and transition is possible + * SASL_BADPARAM -- service name needed + * SASL_BADPROT -- invalid input from client + * ... + */ +LIBSASL_API int sasl_server_step(sasl_conn_t *conn, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen); + +/* check if an apop exchange is valid + * (note this is an optional part of the SASL API) + * if challenge is NULL, just check if APOP is enabled + * inputs: + * challenge -- challenge which was sent to client + * challen -- length of challenge, 0 = strlen(challenge) + * response -- client response, "<user> <digest>" (RFC 1939) + * resplen -- length of response, 0 = strlen(response) + * returns + * SASL_OK -- success + * SASL_BADAUTH -- authentication failed + * SASL_BADPARAM -- missing challenge + * SASL_BADPROT -- protocol error (e.g., response in wrong format) + * SASL_NOVERIFY -- user found, but no verifier + * SASL_NOMECH -- mechanism not supported + * SASL_NOUSER -- user not found + */ +LIBSASL_API int sasl_checkapop(sasl_conn_t *conn, + const char *challenge, unsigned challen, + const char *response, unsigned resplen); + +/* check if a plaintext password is valid + * if user is NULL, check if plaintext passwords are enabled + * inputs: + * user -- user to query in current user_domain + * userlen -- length of username, 0 = strlen(user) + * pass -- plaintext password to check + * passlen -- length of password, 0 = strlen(pass) + * returns + * SASL_OK -- success + * SASL_NOMECH -- mechanism not supported + * SASL_NOVERIFY -- user found, but no verifier + * SASL_NOUSER -- user not found + */ +LIBSASL_API int sasl_checkpass(sasl_conn_t *conn, + const char *user, unsigned userlen, + const char *pass, unsigned passlen); + +/* check if a user exists on server + * conn -- connection context + * service -- registered name of the service using SASL (e.g. "imap") + * user_realm -- permits multiple user realms on server, NULL = default + * user -- NUL terminated user name + * + * returns: + * SASL_OK -- success + * SASL_DISABLED -- account disabled + * SASL_NOUSER -- user not found + * SASL_NOVERIFY -- user found, but no usable mechanism + * SASL_NOMECH -- no mechanisms enabled + * SASL_UNAVAIL -- remote authentication server unavailable, try again later + */ +LIBSASL_API int sasl_user_exists(sasl_conn_t *conn, + const char *service, + const char *user_realm, + const char *user); + +/* set the password for a user + * conn -- SASL connection + * user -- user name + * pass -- plaintext password, may be NULL to remove user + * passlen -- length of password, 0 = strlen(pass) + * oldpass -- NULL will sometimes work + * oldpasslen -- length of password, 0 = strlen(oldpass) + * flags -- see flags below + * + * returns: + * SASL_NOCHANGE -- proper entry already exists + * SASL_NOMECH -- no authdb supports password setting as configured + * SASL_NOVERIFY -- user exists, but no settable password present + * SASL_DISABLED -- account disabled + * SASL_PWLOCK -- password locked + * SASL_WEAKPASS -- password too weak for security policy + * SASL_NOUSERPASS -- user-supplied passwords not permitted + * SASL_FAIL -- OS error + * SASL_BADPARAM -- password too long + * SASL_OK -- successful + */ +LIBSASL_API int sasl_setpass(sasl_conn_t *conn, + const char *user, + const char *pass, unsigned passlen, + const char *oldpass, unsigned oldpasslen, + unsigned flags); +#define SASL_SET_CREATE 0x01 /* create a new entry for user */ +#define SASL_SET_DISABLE 0x02 /* disable user account */ +#define SASL_SET_NOPLAIN 0x04 /* do not store secret in plain text */ +#define SASL_SET_CURMECH_ONLY 0x08 /* set the mechanism specific password only. + fail if no current mechanism */ + +/********************************************************* + * Auxiliary Property Support -- added by cjn 1999-09-29 * + *********************************************************/ + +#define SASL_AUX_END NULL /* last auxiliary property */ + +#define SASL_AUX_ALL "*" /* A special flag to signal user deletion */ + +/* traditional Posix items (should be implemented on Posix systems) */ +#define SASL_AUX_PASSWORD_PROP "userPassword" /* User Password */ +#define SASL_AUX_PASSWORD "*" SASL_AUX_PASSWORD_PROP /* User Password (of authid) */ +#define SASL_AUX_UIDNUM "uidNumber" /* UID number for the user */ +#define SASL_AUX_GIDNUM "gidNumber" /* GID for the user */ +#define SASL_AUX_FULLNAME "gecos" /* full name of the user, unix-style */ +#define SASL_AUX_HOMEDIR "homeDirectory" /* home directory for user */ +#define SASL_AUX_SHELL "loginShell" /* login shell for the user */ + +/* optional additional items (not necessarily implemented) */ +/* single preferred mail address for user canonically-quoted + * RFC821/822 syntax */ +#define SASL_AUX_MAILADDR "mail" +/* path to unix-style mailbox for user */ +#define SASL_AUX_UNIXMBX "mailMessageStore" +/* SMTP mail channel name to use if user authenticates successfully */ +#define SASL_AUX_MAILCHAN "mailSMTPSubmitChannel" + +/* Request a set of auxiliary properties + * conn connection context + * propnames list of auxiliary property names to request ending with + * NULL. + * + * Subsequent calls will add items to the request list. Call with NULL + * to clear the request list. + * + * errors + * SASL_OK -- success + * SASL_BADPARAM -- bad count/conn parameter + * SASL_NOMEM -- out of memory + */ +LIBSASL_API int sasl_auxprop_request(sasl_conn_t *conn, + const char **propnames); + +/* Returns current auxiliary property context. + * Use functions in prop.h to access content + * + * if authentication hasn't completed, property values may be empty/NULL + * + * properties not recognized by active plug-ins will be left empty/NULL + * + * returns NULL if conn is invalid. + */ +LIBSASL_API struct propctx *sasl_auxprop_getctx(sasl_conn_t *conn); + +/* Store the set of auxiliary properties for the given user. + * Use functions in prop.h to set the content. + * + * conn connection context + * ctx property context from prop_new()/prop_request()/prop_set() + * user NUL terminated user + * + * Call with NULL 'ctx' to see if the backend allows storing properties. + * + * errors + * SASL_OK -- success + * SASL_NOMECH -- can not store some/all properties + * SASL_BADPARAM -- bad conn/ctx/user parameter + * SASL_NOMEM -- out of memory + * SASL_FAIL -- failed to store + */ +LIBSASL_API int sasl_auxprop_store(sasl_conn_t *conn, + struct propctx *ctx, const char *user); + +/********************** + * security layer API * + **********************/ + +/* encode a block of data for transmission using security layer, + * returning the input buffer if there is no security layer. + * output is only valid until next call to sasl_encode or sasl_encodev + * returns: + * SASL_OK -- success (returns input if no layer negotiated) + * SASL_NOTDONE -- security layer negotiation not finished + * SASL_BADPARAM -- inputlen is greater than the SASL_MAXOUTBUF + */ +LIBSASL_API int sasl_encode(sasl_conn_t *conn, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen); + +/* encode a block of data for transmission using security layer + * output is only valid until next call to sasl_encode or sasl_encodev + * returns: + * SASL_OK -- success (returns input if no layer negotiated) + * SASL_NOTDONE -- security layer negotiation not finished + * SASL_BADPARAM -- input length is greater than the SASL_MAXOUTBUF + * or no security layer + */ +LIBSASL_API int sasl_encodev(sasl_conn_t *conn, + const struct iovec *invec, unsigned numiov, + const char **output, unsigned *outputlen); + +/* decode a block of data received using security layer + * returning the input buffer if there is no security layer. + * output is only valid until next call to sasl_decode + * + * if outputlen is 0 on return, than the value of output is undefined. + * + * returns: + * SASL_OK -- success (returns input if no layer negotiated) + * SASL_NOTDONE -- security layer negotiation not finished + * SASL_BADMAC -- bad message integrity check + */ +LIBSASL_API int sasl_decode(sasl_conn_t *conn, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen); + +#ifdef __cplusplus +} +#endif + +#endif /* SASL_H */ diff --git a/contrib/libs/sasl/include/sasl/prop.h b/contrib/libs/sasl/include/sasl/prop.h new file mode 100644 index 0000000000..bf091d67d8 --- /dev/null +++ b/contrib/libs/sasl/include/sasl/prop.h @@ -0,0 +1,170 @@ +/* prop.h -- property request/response management routines + * + * Author: Chris Newman + * Removal of implementation-specific details by: Rob Siemborski + * + * This is intended to be used to create a list of properties to request, + * and _then_ request values for all properties. Any change to the request + * list will discard any existing values. This assumption allows a very + * efficient and simple memory model. This was designed for SASL API auxiliary + * property support, but would be fine for other contexts where this property + * model is appropriate. + * + * The "struct propctx" is allocated by prop_new and is a fixed size structure. + * If a prop_init() call were added, it would be reasonable to embed a "struct + * propctx" in another structure. prop_new also allocates a pool of memory + * (in the vbase field) which will be used for an array of "struct propval" + * to list all the requested properties. + * + * Properties may be multi-valued. + */ + +#ifndef PROP_H +#define PROP_H 1 + +/* The following ifdef block is the standard way of creating macros + * which make exporting from a DLL simpler. All files within this DLL + * are compiled with the LIBSASL_EXPORTS symbol defined on the command + * line. this symbol should not be defined on any project that uses + * this DLL. This way any other project whose source files include + * this file see LIBSASL_API functions as being imported from a DLL, + * wheras this DLL sees symbols defined with this macro as being + * exported. */ +/* Under Unix, life is simpler: we just need to mark library functions + * as extern. (Technically, we don't even have to do that.) */ +# define LIBSASL_API extern + +/* Same as above, but used during a variable declaration. */ +# define LIBSASL_VAR extern + +/* the resulting structure for property values + */ +struct propval { + const char *name; /* name of property; NULL = end of list */ + /* same pointer used in request will be used here */ + const char **values; /* list of strings, values == NULL if property not + * found, *values == NULL if property found with + * no values */ + unsigned nvalues; /* total number of value strings */ + unsigned valsize; /* total size in characters of all value strings */ +}; + +/* + * private internal structure + */ +#define PROP_DEFAULT 4 /* default number of propvals to assume */ +struct propctx; + +#ifdef __cplusplus +extern "C" { +#endif + +/* create a property context + * estimate -- an estimate of the storage needed for requests & responses + * 0 will use module default + * returns a new property context on success and NULL on any error + */ +LIBSASL_API struct propctx *prop_new(unsigned estimate); + +/* create new propctx which duplicates the contents of an existing propctx + * returns SASL_OK on success + * possible other return values include: SASL_NOMEM, SASL_BADPARAM + */ +LIBSASL_API int prop_dup(struct propctx *src_ctx, struct propctx **dst_ctx); + +/* Add property names to request + * ctx -- context from prop_new() + * names -- list of property names; must persist until context freed + * or requests cleared (This extends to other contexts that + * are dup'ed from this one, and their children, etc) + * + * NOTE: may clear values from context as side-effect + * returns SASL_OK on success + * possible other return values include: SASL_NOMEM, SASL_BADPARAM + */ +LIBSASL_API int prop_request(struct propctx *ctx, const char **names); + +/* return array of struct propval from the context + * return value persists until next call to + * prop_request, prop_clear or prop_dispose on context + * + * returns NULL on error + */ +LIBSASL_API const struct propval *prop_get(struct propctx *ctx); + +/* Fill in an array of struct propval based on a list of property names + * return value persists until next call to + * prop_request, prop_clear or prop_dispose on context + * returns number of matching properties which were found (values != NULL) + * if a name requested here was never requested by a prop_request, then + * the name field of the associated vals entry will be set to NULL + * + * The vals array MUST be atleast as long as the names array. + * + * returns # of matching properties on success + * possible other return values include: SASL_BADPARAM + */ +LIBSASL_API int prop_getnames(struct propctx *ctx, const char **names, + struct propval *vals); + +/* clear values and optionally requests from property context + * ctx -- property context + * requests -- 0 = don't clear requests, 1 = clear requests + */ +LIBSASL_API void prop_clear(struct propctx *ctx, int requests); + +/* erase the value of a property + */ +LIBSASL_API void prop_erase(struct propctx *ctx, const char *name); + +/* dispose of property context + * ctx -- is disposed and set to NULL; noop if ctx or *ctx is NULL + */ +LIBSASL_API void prop_dispose(struct propctx **ctx); + + +/****fetcher interfaces****/ + +/* format the requested property names into a string + * ctx -- context from prop_new()/prop_request() + * sep -- separator between property names (unused if none requested) + * seplen -- length of separator, if < 0 then strlen(sep) will be used + * outbuf -- output buffer + * outmax -- maximum length of output buffer including NUL terminator + * outlen -- set to length of output string excluding NUL terminator + * returns SASL_OK on success + * returns SASL_BADPARAM or amount of additional space needed on failure + */ +LIBSASL_API int prop_format(struct propctx *ctx, const char *sep, int seplen, + char *outbuf, unsigned outmax, unsigned *outlen); + +/* add a property value to the context + * ctx -- context from prop_new()/prop_request() + * name -- name of property to which value will be added + * if NULL, add to the same name as previous prop_set/setvals call + * value -- a value for the property; will be copied into context + * if NULL, remove existing values + * vallen -- length of value, if <= 0 then strlen(value) will be used + * returns SASL_OK on success + * possible error return values include: SASL_BADPARAM, SASL_NOMEM + */ +LIBSASL_API int prop_set(struct propctx *ctx, const char *name, + const char *value, int vallen); + +/* set the values for a property + * ctx -- context from prop_new()/prop_request() + * name -- name of property to which value will be added + * if NULL, add to the same name as previous prop_set/setvals call + * values -- array of values, ending in NULL. Each value is a NUL terminated + * string + * returns SASL_OK on success + * possible error return values include: SASL_BADPARAM, SASL_NOMEM + */ +LIBSASL_API int prop_setvals(struct propctx *ctx, const char *name, + const char **values); + +#ifdef __cplusplus +} +#endif + +#endif /* PROP_H */ diff --git a/contrib/libs/sasl/include/sasl/sasl.h b/contrib/libs/sasl/include/sasl/sasl.h new file mode 100644 index 0000000000..dc04ca4714 --- /dev/null +++ b/contrib/libs/sasl/include/sasl/sasl.h @@ -0,0 +1,1332 @@ +/* This is a proposed C API for support of SASL + * + *********************************IMPORTANT******************************* + * send email to chris.newman@innosoft.com and cyrus-bugs@andrew.cmu.edu * + * if you need to add new error codes, callback types, property values, * + * etc. It is important to keep the multiple implementations of this * + * API from diverging. * + *********************************IMPORTANT******************************* + * + * Basic Type Summary: + * sasl_conn_t Context for a SASL connection negotiation + * sasl_ssf_t Security layer Strength Factor + * sasl_callback_t A typed client/server callback function and context + * sasl_interact_t A client interaction descriptor + * sasl_secret_t A client password + * sasl_rand_t Random data context structure + * sasl_security_properties_t An application's required security level + * + * Callbacks: + * sasl_getopt_t client/server: Get an option value + * sasl_logmsg_t client/server: Log message handler + * sasl_getsimple_t client: Get user/language list + * sasl_getsecret_t client: Get authentication secret + * sasl_chalprompt_t client: Display challenge and prompt for response + * + * Server only Callbacks: + * sasl_authorize_t user authorization policy callback + * sasl_getconfpath_t get path to search for config file + * sasl_server_userdb_checkpass check password and auxprops in userdb + * sasl_server_userdb_setpass set password in userdb + * sasl_server_canon_user canonicalize username routine + * + * Client/Server Function Summary: + * sasl_done Release all SASL global state + * sasl_dispose Connection done: Dispose of sasl_conn_t + * sasl_getprop Get property (e.g., user name, security layer info) + * sasl_setprop Set property (e.g., external ssf) + * sasl_errdetail Generate string from last error on connection + * sasl_errstring Translate sasl error code to a string + * sasl_encode Encode data to send using security layer + * sasl_decode Decode data received using security layer + * + * Utility functions: + * sasl_encode64 Encode data to send using MIME base64 encoding + * sasl_decode64 Decode data received using MIME base64 encoding + * sasl_erasebuffer Erase a buffer + * + * Client Function Summary: + * sasl_client_init Load and initialize client plug-ins (call once) + * sasl_client_new Initialize client connection context: sasl_conn_t + * sasl_client_start Select mechanism for connection + * sasl_client_step Perform one authentication step + * + * Server Function Summary + * sasl_server_init Load and initialize server plug-ins (call once) + * sasl_server_new Initialize server connection context: sasl_conn_t + * sasl_listmech Create list of available mechanisms + * sasl_server_start Begin an authentication exchange + * sasl_server_step Perform one authentication exchange step + * sasl_checkpass Check a plaintext passphrase + * sasl_checkapop Check an APOP challenge/response (uses pseudo "APOP" + * mechanism similar to CRAM-MD5 mechanism; optional) + * sasl_user_exists Check if user exists + * sasl_setpass Change a password or add a user entry + * sasl_auxprop_request Request auxiliary properties + * sasl_auxprop_getctx Get auxiliary property context for connection + * sasl_auxprop_store Store a set of auxiliary properties + * + * Basic client model: + * 1. client calls sasl_client_init() at startup to load plug-ins + * 2. when connection formed, call sasl_client_new() + * 3. once list of supported mechanisms received from server, client + * calls sasl_client_start(). goto 4a + * 4. client calls sasl_client_step() + * [4a. If SASL_INTERACT, fill in prompts and goto 4 + * -- doesn't happen if callbacks provided] + * 4b. If SASL error, goto 7 or 3 + * 4c. If SASL_OK, continue or goto 6 if last server response was success + * 5. send message to server, wait for response + * 5a. On data or success with server response, goto 4 + * 5b. On failure goto 7 or 3 + * 5c. On success with no server response continue + * 6. continue with application protocol until connection closes + * call sasl_getprop/sasl_encode/sasl_decode() if using security layer + * 7. call sasl_dispose(), may return to step 2 + * 8. call sasl_done() when program terminates + * + * Basic Server model: + * 1. call sasl_server_init() at startup to load plug-ins + * 2. On connection, call sasl_server_new() + * 3. call sasl_listmech() and send list to client] + * 4. after client AUTH command, call sasl_server_start(), goto 5a + * 5. call sasl_server_step() + * 5a. If SASL_CONTINUE, output to client, wait response, repeat 5 + * 5b. If SASL error, then goto 7 + * 5c. If SASL_OK, move on + * 6. continue with application protocol until connection closes + * call sasl_getprop to get username + * call sasl_getprop/sasl_encode/sasl_decode() if using security layer + * 7. call sasl_dispose(), may return to step 2 + * 8. call sasl_done() when program terminates + * + ************************************************* + * IMPORTANT NOTE: server realms / username syntax + * + * If a user name contains a "@", then the rightmost "@" in the user name + * separates the account name from the realm in which this account is + * located. A single server may support multiple realms. If the + * server knows the realm at connection creation time (e.g., a server + * with multiple IP addresses tightly binds one address to a specific + * realm) then that realm must be passed in the user_realm field of + * the sasl_server_new call. If user_realm is non-empty and an + * unqualified user name is supplied, then the canon_user facility is + * expected to append "@" and user_realm to the user name. The canon_user + * facility may treat other characters such as "%" as equivalent to "@". + * + * If the server forbids the use of "@" in user names for other + * purposes, this simplifies security validation. + */ + +#ifndef SASL_H +#define SASL_H 1 + +#include <stddef.h> /* For size_t */ + +/* Keep in sync with win32/common.mak */ +#define SASL_VERSION_MAJOR 2 +#define SASL_VERSION_MINOR 1 +#define SASL_VERSION_STEP 28 + +/* A convenience macro: same as was defined in the OpenLDAP LDAPDB */ +#define SASL_VERSION_FULL ((SASL_VERSION_MAJOR << 16) |\ + (SASL_VERSION_MINOR << 8) | SASL_VERSION_STEP) + +#include "prop.h" + +/************* + * Basic API * + *************/ + +/* SASL result codes: */ +#define SASL_CONTINUE 1 /* another step is needed in authentication */ +#define SASL_OK 0 /* successful result */ +#define SASL_FAIL -1 /* generic failure */ +#define SASL_NOMEM -2 /* memory shortage failure */ +#define SASL_BUFOVER -3 /* overflowed buffer */ +#define SASL_NOMECH -4 /* mechanism not supported */ +#define SASL_BADPROT -5 /* bad protocol / cancel */ +#define SASL_NOTDONE -6 /* can't request info until later in exchange */ +#define SASL_BADPARAM -7 /* invalid parameter supplied */ +#define SASL_TRYAGAIN -8 /* transient failure (e.g., weak key) */ +#define SASL_BADMAC -9 /* integrity check failed */ +#define SASL_NOTINIT -12 /* SASL library not initialized */ + /* -- client only codes -- */ +#define SASL_INTERACT 2 /* needs user interaction */ +#define SASL_BADSERV -10 /* server failed mutual authentication step */ +#define SASL_WRONGMECH -11 /* mechanism doesn't support requested feature */ + /* -- server only codes -- */ +#define SASL_BADAUTH -13 /* authentication failure */ +#define SASL_NOAUTHZ -14 /* authorization failure */ +#define SASL_TOOWEAK -15 /* mechanism too weak for this user */ +#define SASL_ENCRYPT -16 /* encryption needed to use mechanism */ +#define SASL_TRANS -17 /* One time use of a plaintext password will + enable requested mechanism for user */ +#define SASL_EXPIRED -18 /* passphrase expired, has to be reset */ +#define SASL_DISABLED -19 /* account disabled */ +#define SASL_NOUSER -20 /* user not found */ +#define SASL_BADVERS -23 /* version mismatch with plug-in */ +#define SASL_UNAVAIL -24 /* remote authentication server unavailable */ +#define SASL_NOVERIFY -26 /* user exists, but no verifier for user */ + /* -- codes for password setting -- */ +#define SASL_PWLOCK -21 /* passphrase locked */ +#define SASL_NOCHANGE -22 /* requested change was not needed */ +#define SASL_WEAKPASS -27 /* passphrase is too weak for security policy */ +#define SASL_NOUSERPASS -28 /* user supplied passwords not permitted */ +#define SASL_NEED_OLD_PASSWD -29 /* sasl_setpass needs old password in order + to perform password change */ +#define SASL_CONSTRAINT_VIOLAT -30 /* a property can't be stored, + because of some constrains/policy violation */ + +#define SASL_BADBINDING -32 /* channel binding failure */ +#define SASL_CONFIGERR -100 /* error when parsing configuration file */ + +/* max size of a sasl mechanism name */ +#define SASL_MECHNAMEMAX 20 + +#ifdef _WIN32 +/* Define to have the same layout as a WSABUF */ +#ifndef STRUCT_IOVEC_DEFINED +#define STRUCT_IOVEC_DEFINED 1 +struct iovec { + long iov_len; + char *iov_base; +}; +#endif +#else +struct iovec; /* Defined in OS headers */ +#endif + + +/* per-connection SASL negotiation state for client or server + */ +typedef struct sasl_conn sasl_conn_t; + +/* Plain text password structure. + * len is the length of the password, data is the text. + */ +typedef struct sasl_secret { + unsigned long len; + unsigned char data[1]; /* variable sized */ +} sasl_secret_t; + +/* random data context structure + */ +typedef struct sasl_rand_s sasl_rand_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************** + * Configure Basic Services * + ****************************/ + +/* the following functions are used to adjust how allocation and mutexes work + * they must be called before all other SASL functions: + */ + +#include <sys/types.h> + +/* memory allocation functions which may optionally be replaced: + */ +typedef void *sasl_malloc_t(size_t); +typedef void *sasl_calloc_t(size_t, size_t); +typedef void *sasl_realloc_t(void *, size_t); +typedef void sasl_free_t(void *); + +LIBSASL_API void sasl_set_alloc(sasl_malloc_t *, + sasl_calloc_t *, + sasl_realloc_t *, + sasl_free_t *); + +/* mutex functions which may optionally be replaced: + * sasl_mutex_alloc allocates a mutex structure + * sasl_mutex_lock blocks until mutex locked + * returns -1 on deadlock or parameter error + * returns 0 on success + * sasl_mutex_unlock unlocks mutex if it's locked + * returns -1 if not locked or parameter error + * returns 0 on success + * sasl_mutex_free frees a mutex structure + */ +typedef void *sasl_mutex_alloc_t(void); +typedef int sasl_mutex_lock_t(void *mutex); +typedef int sasl_mutex_unlock_t(void *mutex); +typedef void sasl_mutex_free_t(void *mutex); +LIBSASL_API void sasl_set_mutex(sasl_mutex_alloc_t *, sasl_mutex_lock_t *, + sasl_mutex_unlock_t *, sasl_mutex_free_t *); + +/***************************** + * Security preference types * + *****************************/ + +/* security layer strength factor -- an unsigned integer usable by the caller + * to specify approximate security layer strength desired. Roughly + * correlated to effective key length for encryption. + * 0 = no protection + * 1 = integrity protection only + * 40 = 40-bit DES or 40-bit RC2/RC4 + * 56 = DES + * 112 = triple-DES + * 128 = 128-bit RC2/RC4/BLOWFISH + * 256 = baseline AES + */ +typedef unsigned sasl_ssf_t; + +/* usage flags provided to sasl_server_new and sasl_client_new: + */ +#define SASL_SUCCESS_DATA 0x0004 /* server supports data on success */ +#define SASL_NEED_PROXY 0x0008 /* require a mech that allows proxying */ +#define SASL_NEED_HTTP 0x0010 /* require a mech that can do HTTP auth */ + +/*************************** + * Security Property Types * + ***************************/ + +/* Structure specifying the client or server's security policy + * and optional additional properties. + */ + +/* These are the various security flags apps can specify. */ +/* NOPLAINTEXT -- don't permit mechanisms susceptible to simple + * passive attack (e.g., PLAIN, LOGIN) + * NOACTIVE -- protection from active (non-dictionary) attacks + * during authentication exchange. + * Authenticates server. + * NODICTIONARY -- don't permit mechanisms susceptible to passive + * dictionary attack + * FORWARD_SECRECY -- require forward secrecy between sessions + * (breaking one won't help break next) + * NOANONYMOUS -- don't permit mechanisms that allow anonymous login + * PASS_CREDENTIALS -- require mechanisms which pass client + * credentials, and allow mechanisms which can pass + * credentials to do so + * MUTUAL_AUTH -- require mechanisms which provide mutual + * authentication + */ +#define SASL_SEC_NOPLAINTEXT 0x0001 +#define SASL_SEC_NOACTIVE 0x0002 +#define SASL_SEC_NODICTIONARY 0x0004 +#define SASL_SEC_FORWARD_SECRECY 0x0008 +#define SASL_SEC_NOANONYMOUS 0x0010 +#define SASL_SEC_PASS_CREDENTIALS 0x0020 +#define SASL_SEC_MUTUAL_AUTH 0x0040 +#define SASL_SEC_MAXIMUM 0xFFFF + +/* This is used when adding hash size to the security_flags field */ +/* NB: hash size is in bits */ +#define SASL_SET_HASH_STRENGTH_BITS(x) (((x) / 8) << 16) + +/* NB: This value is in bytes */ +#define SASL_GET_HASH_STRENGTH(x) ((x) >> 16) + +typedef struct sasl_security_properties +{ + /* security strength factor + * min_ssf = minimum acceptable final level + * max_ssf = maximum acceptable final level + */ + sasl_ssf_t min_ssf; + sasl_ssf_t max_ssf; + + /* Maximum security layer receive buffer size. + * 0=security layer not supported + */ + unsigned maxbufsize; + + /* bitfield for attacks to protect against */ + unsigned security_flags; + + /* NULL terminated array of additional property names, values */ + const char **property_names; + const char **property_values; +} sasl_security_properties_t; + +/****************** + * Callback types * + ******************/ + +/* + * Extensible type for a client/server callbacks + * id -- identifies callback type + * proc -- procedure call arguments vary based on id + * context -- context passed to procedure + */ +/* Note that any memory that is allocated by the callback needs to be + * freed by the application, be it via function call or interaction. + * + * It may be freed after sasl_*_step returns SASL_OK. if the mechanism + * requires this information to persist (for a security layer, for example) + * it must maintain a private copy. + */ +typedef struct sasl_callback { + /* Identifies the type of the callback function. + * Mechanisms must ignore callbacks with id's they don't recognize. + */ + unsigned long id; + int (*proc)(void); /* Callback function. Types of arguments vary by 'id' */ + void *context; +} sasl_callback_t; + +/* callback ids & functions: + */ +#define SASL_CB_LIST_END 0 /* end of list */ + +/* option reading callback -- this allows a SASL configuration to be + * encapsulated in the caller's configuration system. Some implementations + * may use default config file(s) if this is omitted. Configuration items + * may be plugin-specific and are arbitrary strings. + * + * inputs: + * context -- option context from callback record + * plugin_name -- name of plugin (NULL = general SASL option) + * option -- name of option + * output: + * result -- set to result which persists until next getopt in + * same thread, unchanged if option not found + * len -- length of result (may be NULL) + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_getopt_t(void *context, const char *plugin_name, + const char *option, + const char **result, unsigned *len); +#define SASL_CB_GETOPT 1 + +/* Logging levels for use with the logging callback function. */ +#define SASL_LOG_NONE 0 /* don't log anything */ +#define SASL_LOG_ERR 1 /* log unusual errors (default) */ +#define SASL_LOG_FAIL 2 /* log all authentication failures */ +#define SASL_LOG_WARN 3 /* log non-fatal warnings */ +#define SASL_LOG_NOTE 4 /* more verbose than LOG_WARN */ +#define SASL_LOG_DEBUG 5 /* more verbose than LOG_NOTE */ +#define SASL_LOG_TRACE 6 /* traces of internal protocols */ +#define SASL_LOG_PASS 7 /* traces of internal protocols, including + * passwords */ + +/* logging callback -- this allows plugins and the middleware to + * log operations they perform. + * inputs: + * context -- logging context from the callback record + * level -- logging level; see above + * message -- message to log + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_log_t(void *context, + int level, + const char *message); +#define SASL_CB_LOG 2 + +/* getpath callback -- this allows applications to specify the + * colon-separated path to search for plugins (by default, + * taken from an implementation-specific location). + * inputs: + * context -- getpath context from the callback record + * outputs: + * path -- colon seperated path + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_getpath_t(void *context, + const char **path); + +#define SASL_CB_GETPATH 3 + +/* verify file callback -- this allows applications to check if they + * want SASL to use files, file by file. This is intended to allow + * applications to sanity check the environment to make sure plugins + * or the configuration file can't be written to, etc. + * inputs: + * context -- verifypath context from the callback record + * file -- full path to file to verify + * type -- type of file to verify (see below) + + * returns: + * SASL_OK -- no error (file can safely be used) + * SASL_CONTINUE -- continue WITHOUT using this file + * SASL_FAIL -- error + */ + +/* these are the types of files libsasl will ask about */ +typedef enum { + SASL_VRFY_PLUGIN=0, /* a DLL/shared library plug-in */ + SASL_VRFY_CONF=1, /* a configuration file */ + SASL_VRFY_PASSWD=2, /* a password storage file/db */ + SASL_VRFY_OTHER=3 /* some other file */ +} sasl_verify_type_t; + +typedef int sasl_verifyfile_t(void *context, + const char *file, sasl_verify_type_t type); +#define SASL_CB_VERIFYFILE 4 + +/* getconfpath callback -- this allows applications to specify the + * colon-separated path to search for config files (by default, + * taken from the SASL_CONF_PATH environment variable). + * inputs: + * context -- getconfpath context from the callback record + * outputs: + * path -- colon seperated path (allocated on the heap; the + * library will free it using the sasl_free_t * + * passed to sasl_set_callback, or the standard free() + * library call). + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_getconfpath_t(void *context, + char **path); + +#define SASL_CB_GETCONFPATH 5 + +/* client/user interaction callbacks: + */ +/* Simple prompt -- result must persist until next call to getsimple on + * same connection or until connection context is disposed + * inputs: + * context -- context from callback structure + * id -- callback id + * outputs: + * result -- set to NUL terminated string + * NULL = user cancel + * len -- length of result + * returns SASL_OK + */ +typedef int sasl_getsimple_t(void *context, int id, + const char **result, unsigned *len); +#define SASL_CB_USER 0x4001 /* client user identity to login as */ +#define SASL_CB_AUTHNAME 0x4002 /* client authentication name */ +#define SASL_CB_LANGUAGE 0x4003 /* comma separated list of RFC 1766 + * language codes in order of preference + * to be used to localize client prompts + * or server error codes */ +#define SASL_CB_CNONCE 0x4007 /* caller supplies client-nonce + * primarily for testing purposes */ + +/* get a sasl_secret_t (plaintext password with length) + * inputs: + * conn -- connection context + * context -- context from callback structure + * id -- callback id + * outputs: + * psecret -- set to NULL to cancel + * set to password structure which must persist until + * next call to getsecret in same connection, but middleware + * will erase password data when it's done with it. + * returns SASL_OK + */ +typedef int sasl_getsecret_t(sasl_conn_t *conn, void *context, int id, + sasl_secret_t **psecret); +#define SASL_CB_PASS 0x4004 /* client passphrase-based secret */ + + +/* prompt for input in response to a challenge. + * input: + * context -- context from callback structure + * id -- callback id + * challenge -- server challenge + * output: + * result -- NUL terminated result, NULL = user cancel + * len -- length of result + * returns SASL_OK + */ +typedef int sasl_chalprompt_t(void *context, int id, + const char *challenge, + const char *prompt, const char *defresult, + const char **result, unsigned *len); +#define SASL_CB_ECHOPROMPT 0x4005 /* challenge and client enterred result */ +#define SASL_CB_NOECHOPROMPT 0x4006 /* challenge and client enterred result */ + +/* prompt (or autoselect) the realm to do authentication in. + * may get a list of valid realms. + * input: + * context -- context from callback structure + * id -- callback id + * availrealms -- available realms; string list; NULL terminated + * list may be empty. + * output: + * result -- NUL terminated realm; NULL is equivalent to "" + * returns SASL_OK + * result must persist until the next callback + */ +typedef int sasl_getrealm_t(void *context, int id, + const char **availrealms, + const char **result); +#define SASL_CB_GETREALM (0x4008) /* realm to attempt authentication in */ + +/* server callbacks: + */ + +/* improved callback to verify authorization; + * canonicalization now handled elsewhere + * conn -- connection context + * requested_user -- the identity/username to authorize (NUL terminated) + * rlen -- length of requested_user + * auth_identity -- the identity associated with the secret (NUL terminated) + * alen -- length of auth_identity + * default_realm -- default user realm, as passed to sasl_server_new if + * urlen -- length of default realm + * propctx -- auxiliary properties + * returns SASL_OK on success, + * SASL_NOAUTHZ or other SASL response on failure + */ +typedef int sasl_authorize_t(sasl_conn_t *conn, + void *context, + const char *requested_user, unsigned rlen, + const char *auth_identity, unsigned alen, + const char *def_realm, unsigned urlen, + struct propctx *propctx); +#define SASL_CB_PROXY_POLICY 0x8001 + +/* functions for "userdb" based plugins to call to get/set passwords. + * the location for the passwords is determined by the caller or middleware. + * plug-ins may get passwords from other locations. + */ + +/* callback to verify a plaintext password against the caller-supplied + * user database. This is necessary to allow additional <method>s for + * encoding of the userPassword property. + * user -- NUL terminated user name with user@realm syntax + * pass -- password to check (may not be NUL terminated) + * passlen -- length of password to check + * propctx -- auxiliary properties for user + */ +typedef int sasl_server_userdb_checkpass_t(sasl_conn_t *conn, + void *context, + const char *user, + const char *pass, + unsigned passlen, + struct propctx *propctx); +#define SASL_CB_SERVER_USERDB_CHECKPASS (0x8005) + +/* callback to store/change a plaintext password in the user database + * user -- NUL terminated user name with user@realm syntax + * pass -- password to store (may not be NUL terminated) + * passlen -- length of password to store + * propctx -- auxiliary properties (not stored) + * flags -- see SASL_SET_* flags below (SASL_SET_CREATE optional) + */ +typedef int sasl_server_userdb_setpass_t(sasl_conn_t *conn, + void *context, + const char *user, + const char *pass, + unsigned passlen, + struct propctx *propctx, + unsigned flags); +#define SASL_CB_SERVER_USERDB_SETPASS (0x8006) + +/* callback for a server-supplied user canonicalization function. + * + * This function is called directly after the mechanism has the + * authentication and authorization IDs. It is called before any + * User Canonicalization plugin is called. It has the responsibility + * of copying its output into the provided output buffers. + * + * in, inlen -- user name to canonicalize, may not be NUL terminated + * may be same buffer as out + * flags -- not currently used, supplied by auth mechanism + * user_realm -- the user realm (may be NULL in case of client) + * out -- buffer to copy user name + * out_max -- max length of user name + * out_len -- set to length of user name + * + * returns + * SASL_OK on success + * SASL_BADPROT username contains invalid character + */ + +/* User Canonicalization Function Flags */ + +#define SASL_CU_NONE 0x00 /* Not a valid flag to pass */ +/* One of the following two is required */ +#define SASL_CU_AUTHID 0x01 +#define SASL_CU_AUTHZID 0x02 + +/* Combine the following with SASL_CU_AUTHID, if you don't want + to fail if auxprop returned SASL_NOUSER/SASL_NOMECH. */ +#define SASL_CU_EXTERNALLY_VERIFIED 0x04 + +#define SASL_CU_OVERRIDE 0x08 /* mapped to SASL_AUXPROP_OVERRIDE */ + +/* The following CU flags are passed "as is" down to auxprop lookup */ +#define SASL_CU_ASIS_MASK 0xFFF0 +/* NOTE: Keep in sync with SASL_AUXPROP_<XXX> flags */ +#define SASL_CU_VERIFY_AGAINST_HASH 0x10 + + +typedef int sasl_canon_user_t(sasl_conn_t *conn, + void *context, + const char *in, unsigned inlen, + unsigned flags, + const char *user_realm, + char *out, + unsigned out_max, unsigned *out_len); + +#define SASL_CB_CANON_USER (0x8007) + +/********************************** + * Common Client/server functions * + **********************************/ + +/* Types of paths to set (see sasl_set_path below). */ +#define SASL_PATH_TYPE_PLUGIN 0 +#define SASL_PATH_TYPE_CONFIG 1 + +/* a simpler way to set plugin path or configuration file path + * without the need to set sasl_getpath_t callback. + * + * This function can be called before sasl_server_init/sasl_client_init. + */ +LIBSASL_API int sasl_set_path (int path_type, char * path); + +/* get sasl library version information + * implementation is a vendor-defined string + * version is a vender-defined representation of the version #. + * + * This function is being deprecated in favor of sasl_version_info. */ +LIBSASL_API void sasl_version(const char **implementation, + int *version); + +/* Extended version of sasl_version(). + * + * This function is to be used + * for library version display and logging + * for bug workarounds in old library versions + * + * The sasl_version_info is not to be used for API feature detection. + * + * All parameters are optional. If NULL is specified, the value is not returned. + */ +LIBSASL_API void sasl_version_info (const char **implementation, + const char **version_string, + int *version_major, + int *version_minor, + int *version_step, + int *version_patch); + +/* dispose of all SASL plugins. Connection + * states have to be disposed of before calling this. + * + * This function is DEPRECATED in favour of sasl_server_done/ + * sasl_client_done. + */ +LIBSASL_API void sasl_done(void); + +/* dispose of all SASL plugins. Connection + * states have to be disposed of before calling this. + * This function should be called instead of sasl_done(), + whenever possible. + */ +LIBSASL_API int sasl_server_done(void); + +/* dispose of all SASL plugins. Connection + * states have to be disposed of before calling this. + * This function should be called instead of sasl_done(), + whenever possible. + */ +LIBSASL_API int sasl_client_done(void); + +/* dispose connection state, sets it to NULL + * checks for pointer to NULL + */ +LIBSASL_API void sasl_dispose(sasl_conn_t **pconn); + +/* translate an error number into a string + * input: + * saslerr -- the error number + * langlist -- comma separated list of RFC 1766 languages (may be NULL) + * results: + * outlang -- the language actually used (may be NULL if don't care) + * returns: + * the error message in UTF-8 (only the US-ASCII subset if langlist is NULL) + */ +LIBSASL_API const char *sasl_errstring(int saslerr, + const char *langlist, + const char **outlang); + +/* get detail about the last error that occurred on a connection + * text is sanitized so it's suitable to send over the wire + * (e.g., no distinction between SASL_BADAUTH and SASL_NOUSER) + * input: + * conn -- mandatory connection context + * returns: + * the error message in UTF-8 (only the US-ASCII subset permitted if no + * SASL_CB_LANGUAGE callback is present) + */ +LIBSASL_API const char *sasl_errdetail(sasl_conn_t *conn); + +/* set the error string which will be returned by sasl_errdetail() using + * syslog()-style formatting (e.g. printf-style with %m as most recent + * errno error) + * + * primarily for use by server callbacks such as the sasl_authorize_t + * callback and internally to plug-ins + * + * This will also trigger a call to the SASL logging callback (if any) + * with a level of SASL_LOG_FAIL unless the SASL_NOLOG flag is set. + * + * Messages should be sensitive to the current language setting. If there + * is no SASL_CB_LANGUAGE callback messages MUST be US-ASCII otherwise UTF-8 + * is used and use of RFC 2482 for mixed-language text is encouraged. + * + * if conn is NULL, function does nothing + */ +LIBSASL_API void sasl_seterror(sasl_conn_t *conn, unsigned flags, + const char *fmt, ...); +#define SASL_NOLOG 0x01 + +/* get property from SASL connection state + * propnum -- property number + * pvalue -- pointer to value + * returns: + * SASL_OK -- no error + * SASL_NOTDONE -- property not available yet + * SASL_BADPARAM -- bad property number + */ +LIBSASL_API int sasl_getprop(sasl_conn_t *conn, int propnum, + const void **pvalue); +#define SASL_USERNAME 0 /* pointer to NUL terminated user name */ +#define SASL_SSF 1 /* security layer security strength factor, + * if 0, call to sasl_encode, sasl_decode + * unnecessary */ +#define SASL_MAXOUTBUF 2 /* security layer max output buf unsigned */ +#define SASL_DEFUSERREALM 3 /* default realm passed to server_new */ + /* or set with setprop */ +#define SASL_GETOPTCTX 4 /* context for getopt callback */ +#define SASL_CALLBACK 7 /* current callback function list */ +#define SASL_IPLOCALPORT 8 /* iplocalport string passed to server_new */ +#define SASL_IPREMOTEPORT 9 /* ipremoteport string passed to server_new */ + +/* This returns a string which is either empty or has an error message + * from sasl_seterror (e.g., from a plug-in or callback). It differs + * from the result of sasl_errdetail() which also takes into account the + * last return status code. + */ +#define SASL_PLUGERR 10 + +/* a handle to any delegated credentials or NULL if none is present + * is returned by the mechanism. The user will probably need to know + * which mechanism was used to actually known how to make use of them + * currently only implemented for the gssapi mechanism */ +#define SASL_DELEGATEDCREDS 11 + +#define SASL_SERVICE 12 /* service passed to sasl_*_new */ +#define SASL_SERVERFQDN 13 /* serverFQDN passed to sasl_*_new */ +#define SASL_AUTHSOURCE 14 /* name of auth source last used, useful + * for failed authentication tracking */ +#define SASL_MECHNAME 15 /* active mechanism name, if any */ +#define SASL_AUTHUSER 16 /* authentication/admin user */ +#define SASL_APPNAME 17 /* application name (used for logging/ + configuration), same as appname parameter + to sasl_server_init */ + +/* GSS-API credential handle for sasl_client_step() or sasl_server_step(). + * The application is responsible for releasing this credential handle. */ +#define SASL_GSS_CREDS 18 + +/* GSS name (gss_name_t) of the peer, as output by gss_inquire_context() + * or gss_accept_sec_context(). + * On server end this is similar to SASL_USERNAME, but the gss_name_t + * structure can contain additional attributes associated with the peer. + */ +#define SASL_GSS_PEER_NAME 19 + +/* Local GSS name (gss_name_t) as output by gss_inquire_context(). This + * is particularly useful for servers that respond to multiple names. */ +#define SASL_GSS_LOCAL_NAME 20 + +/* Channel binding information. Memory is managed by the caller. */ +typedef struct sasl_channel_binding { + const char *name; + int critical; + unsigned long len; + const unsigned char *data; +} sasl_channel_binding_t; + +#define SASL_CHANNEL_BINDING 21 + +/* HTTP Request (RFC 2616) - ONLY used for HTTP Digest Auth (RFC 2617) */ +typedef struct sasl_http_request { + const char *method; /* HTTP Method */ + const char *uri; /* request-URI */ + const unsigned char *entity; /* entity-body */ + unsigned long elen; /* entity-body length */ + unsigned non_persist; /* Is it a non-persistent connection? */ +} sasl_http_request_t; + +#define SASL_HTTP_REQUEST 22 + +/* set property in SASL connection state + * returns: + * SASL_OK -- value set + * SASL_BADPARAM -- invalid property or value + */ +LIBSASL_API int sasl_setprop(sasl_conn_t *conn, + int propnum, + const void *value); +#define SASL_SSF_EXTERNAL 100 /* external SSF active (sasl_ssf_t *) */ +#define SASL_SEC_PROPS 101 /* sasl_security_properties_t */ +#define SASL_AUTH_EXTERNAL 102 /* external authentication ID (const char *) */ + +/* If the SASL_AUTH_EXTERNAL value is non-NULL, then a special version of the + * EXTERNAL mechanism is enabled (one for server-embedded EXTERNAL mechanisms). + * Otherwise, the EXTERNAL mechanism will be absent unless a plug-in + * including EXTERNAL is present. + */ + +/* do precalculations during an idle period or network round trip + * may pass NULL to precompute for some mechanisms prior to connect + * returns 1 if action taken, 0 if no action taken + */ +LIBSASL_API int sasl_idle(sasl_conn_t *conn); + +/************** + * Client API * + **************/ + +/* list of client interactions with user for caller to fill in + */ +typedef struct sasl_interact { + unsigned long id; /* same as client/user callback ID */ + const char *challenge; /* presented to user (e.g. OTP challenge) */ + const char *prompt; /* presented to user (e.g. "Username: ") */ + const char *defresult; /* default result string */ + const void *result; /* set to point to result */ + unsigned len; /* set to length of result */ +} sasl_interact_t; + +/* initialize the SASL client drivers + * callbacks -- base callbacks for all client connections; + * must include getopt callback + * returns: + * SASL_OK -- Success + * SASL_NOMEM -- Not enough memory + * SASL_BADVERS -- Mechanism version mismatch + * SASL_BADPARAM -- missing getopt callback or error in config file + * SASL_NOMECH -- No mechanisms available + * ... + */ +LIBSASL_API int sasl_client_init(const sasl_callback_t *callbacks); + +/* initialize a client exchange based on the specified mechanism + * service -- registered name of the service using SASL (e.g. "imap") + * serverFQDN -- the fully qualified domain name of the server + * iplocalport -- client IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * ipremoteport -- server IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * prompt_supp -- list of client interactions supported + * may also include sasl_getopt_t context & call + * NULL prompt_supp = user/pass via SASL_INTERACT only + * NULL proc = interaction supported via SASL_INTERACT + * flags -- server usage flags (see above) + * in/out: + * pconn -- connection negotiation structure + * pointer to NULL => allocate new + * + * Returns: + * SASL_OK -- success + * SASL_NOMECH -- no mechanism meets requested properties + * SASL_NOMEM -- not enough memory + */ +LIBSASL_API int sasl_client_new(const char *service, + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *prompt_supp, + unsigned flags, + sasl_conn_t **pconn); + +/* select a mechanism for a connection + * mechlist -- mechanisms server has available (punctuation ignored) + * if NULL, then discard cached info and retry last mech + * output: + * prompt_need -- on SASL_INTERACT, list of prompts needed to continue + * may be NULL if callbacks provided + * clientout -- the initial client response to send to the server + * will be valid until next call to client_start/client_step + * NULL if mech doesn't include initial client challenge + * mech -- set to mechansm name of selected mechanism (may be NULL) + * + * Returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + * SASL_NOMECH -- no mechanism meets requested properties + * SASL_INTERACT -- user interaction needed to fill in prompt_need list + */ +LIBSASL_API int sasl_client_start(sasl_conn_t *conn, + const char *mechlist, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + const char **mech); + +/* do a single authentication step. + * serverin -- the server message received by the client, MUST have a NUL + * sentinel, not counted by serverinlen + * output: + * prompt_need -- on SASL_INTERACT, list of prompts needed to continue + * clientout -- the client response to send to the server + * will be valid until next call to client_start/client_step + * + * returns: + * SASL_OK -- success + * SASL_INTERACT -- user interaction needed to fill in prompt_need list + * SASL_BADPROT -- server protocol incorrect/cancelled + * SASL_BADSERV -- server failed mutual auth + */ +LIBSASL_API int sasl_client_step(sasl_conn_t *conn, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen); + +/************** + * Server API * + **************/ + +/* initialize server drivers, done once per process + * callbacks -- callbacks for all server connections; must include + * getopt callback + * appname -- name of calling application (for lower level logging) + * results: + * state -- server state + * returns: + * SASL_OK -- success + * SASL_BADPARAM -- error in config file + * SASL_NOMEM -- memory failure + * SASL_BADVERS -- Mechanism version mismatch + */ +LIBSASL_API int sasl_server_init(const sasl_callback_t *callbacks, + const char *appname); + +/* IP/port syntax: + * a.b.c.d;p where a-d are 0-255 and p is 0-65535 port number. + * e:f:g:h:i:j:k:l;p where e-l are 0000-ffff lower-case hexidecimal + * e:f:g:h:i:j:a.b.c.d;p alternate syntax for previous + * + * Note that one or more "0" fields in f-k can be replaced with "::" + * Thus: e:f:0000:0000:0000:j:k:l;p + * can be abbreviated: e:f::j:k:l;p + * + * A buffer of size 52 is adequate for the longest format with NUL terminator. + */ + +/* create context for a single SASL connection + * service -- registered name of the service using SASL (e.g. "imap") + * serverFQDN -- Fully qualified domain name of server. NULL means use + * gethostname() or equivalent. + * Useful for multi-homed servers. + * user_realm -- permits multiple user realms on server, NULL = default + * iplocalport -- server IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * ipremoteport -- client IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * callbacks -- callbacks (e.g., authorization, lang, new getopt context) + * flags -- usage flags (see above) + * returns: + * pconn -- new connection context + * + * returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + */ +LIBSASL_API int sasl_server_new(const char *service, + const char *serverFQDN, + const char *user_realm, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *callbacks, + unsigned flags, + sasl_conn_t **pconn); + +/* Return an array of NUL-terminated strings, terminated by a NULL pointer, + * which lists all possible mechanisms that the library can supply + * + * Returns NULL on failure. */ +LIBSASL_API const char ** sasl_global_listmech(void); + +/* This returns a list of mechanisms in a NUL-terminated string + * conn -- the connection to list mechanisms for (either client + * or server) + * user -- restricts mechanisms to those available to that user + * (may be NULL, not used for client case) + * prefix -- appended to beginning of result + * sep -- appended between mechanisms + * suffix -- appended to end of result + * results: + * result -- NUL terminated result which persists until next + * call to sasl_listmech for this sasl_conn_t + * plen -- gets length of result (excluding NUL), may be NULL + * pcount -- gets number of mechanisms, may be NULL + * + * returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + * SASL_NOMECH -- no enabled mechanisms + */ +LIBSASL_API int sasl_listmech(sasl_conn_t *conn, + const char *user, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount); + +/* start a mechanism exchange within a connection context + * mech -- the mechanism name client requested + * clientin -- client initial response (NUL terminated), NULL if empty + * clientinlen -- length of initial response + * serverout -- initial server challenge, NULL if done + * (library handles freeing this string) + * serveroutlen -- length of initial server challenge + * output: + * pconn -- the connection negotiation state on success + * + * Same returns as sasl_server_step() or + * SASL_NOMECH if mechanism not available. + */ +LIBSASL_API int sasl_server_start(sasl_conn_t *conn, + const char *mech, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen); + +/* perform one step of the SASL exchange + * inputlen & input -- client data + * NULL on first step if no optional client step + * outputlen & output -- set to the server data to transmit + * to the client in the next step + * (library handles freeing this) + * + * returns: + * SASL_OK -- exchange is complete. + * SASL_CONTINUE -- indicates another step is necessary. + * SASL_TRANS -- entry for user exists, but not for mechanism + * and transition is possible + * SASL_BADPARAM -- service name needed + * SASL_BADPROT -- invalid input from client + * ... + */ +LIBSASL_API int sasl_server_step(sasl_conn_t *conn, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen); + +/* check if an apop exchange is valid + * (note this is an optional part of the SASL API) + * if challenge is NULL, just check if APOP is enabled + * inputs: + * challenge -- challenge which was sent to client + * challen -- length of challenge, 0 = strlen(challenge) + * response -- client response, "<user> <digest>" (RFC 1939) + * resplen -- length of response, 0 = strlen(response) + * returns + * SASL_OK -- success + * SASL_BADAUTH -- authentication failed + * SASL_BADPARAM -- missing challenge + * SASL_BADPROT -- protocol error (e.g., response in wrong format) + * SASL_NOVERIFY -- user found, but no verifier + * SASL_NOMECH -- mechanism not supported + * SASL_NOUSER -- user not found + */ +LIBSASL_API int sasl_checkapop(sasl_conn_t *conn, + const char *challenge, unsigned challen, + const char *response, unsigned resplen); + +/* check if a plaintext password is valid + * if user is NULL, check if plaintext passwords are enabled + * inputs: + * user -- user to query in current user_domain + * userlen -- length of username, 0 = strlen(user) + * pass -- plaintext password to check + * passlen -- length of password, 0 = strlen(pass) + * returns + * SASL_OK -- success + * SASL_NOMECH -- mechanism not supported + * SASL_NOVERIFY -- user found, but no verifier + * SASL_NOUSER -- user not found + */ +LIBSASL_API int sasl_checkpass(sasl_conn_t *conn, + const char *user, unsigned userlen, + const char *pass, unsigned passlen); + +/* check if a user exists on server + * conn -- connection context + * service -- registered name of the service using SASL (e.g. "imap") + * user_realm -- permits multiple user realms on server, NULL = default + * user -- NUL terminated user name + * + * returns: + * SASL_OK -- success + * SASL_DISABLED -- account disabled + * SASL_NOUSER -- user not found + * SASL_NOVERIFY -- user found, but no usable mechanism + * SASL_NOMECH -- no mechanisms enabled + * SASL_UNAVAIL -- remote authentication server unavailable, try again later + */ +LIBSASL_API int sasl_user_exists(sasl_conn_t *conn, + const char *service, + const char *user_realm, + const char *user); + +/* set the password for a user + * conn -- SASL connection + * user -- user name + * pass -- plaintext password, may be NULL to remove user + * passlen -- length of password, 0 = strlen(pass) + * oldpass -- NULL will sometimes work + * oldpasslen -- length of password, 0 = strlen(oldpass) + * flags -- see flags below + * + * returns: + * SASL_NOCHANGE -- proper entry already exists + * SASL_NOMECH -- no authdb supports password setting as configured + * SASL_NOVERIFY -- user exists, but no settable password present + * SASL_DISABLED -- account disabled + * SASL_PWLOCK -- password locked + * SASL_WEAKPASS -- password too weak for security policy + * SASL_NOUSERPASS -- user-supplied passwords not permitted + * SASL_FAIL -- OS error + * SASL_BADPARAM -- password too long + * SASL_OK -- successful + */ +LIBSASL_API int sasl_setpass(sasl_conn_t *conn, + const char *user, + const char *pass, unsigned passlen, + const char *oldpass, unsigned oldpasslen, + unsigned flags); +#define SASL_SET_CREATE 0x01 /* create a new entry for user */ +#define SASL_SET_DISABLE 0x02 /* disable user account */ +#define SASL_SET_NOPLAIN 0x04 /* do not store secret in plain text */ +#define SASL_SET_CURMECH_ONLY 0x08 /* set the mechanism specific password only. + fail if no current mechanism */ + +/********************************************************* + * Auxiliary Property Support -- added by cjn 1999-09-29 * + *********************************************************/ + +#define SASL_AUX_END NULL /* last auxiliary property */ + +#define SASL_AUX_ALL "*" /* A special flag to signal user deletion */ + +/* traditional Posix items (should be implemented on Posix systems) */ +#define SASL_AUX_PASSWORD_PROP "userPassword" /* User Password */ +#define SASL_AUX_PASSWORD "*" SASL_AUX_PASSWORD_PROP /* User Password (of authid) */ +#define SASL_AUX_UIDNUM "uidNumber" /* UID number for the user */ +#define SASL_AUX_GIDNUM "gidNumber" /* GID for the user */ +#define SASL_AUX_FULLNAME "gecos" /* full name of the user, unix-style */ +#define SASL_AUX_HOMEDIR "homeDirectory" /* home directory for user */ +#define SASL_AUX_SHELL "loginShell" /* login shell for the user */ + +/* optional additional items (not necessarily implemented) */ +/* single preferred mail address for user canonically-quoted + * RFC821/822 syntax */ +#define SASL_AUX_MAILADDR "mail" +/* path to unix-style mailbox for user */ +#define SASL_AUX_UNIXMBX "mailMessageStore" +/* SMTP mail channel name to use if user authenticates successfully */ +#define SASL_AUX_MAILCHAN "mailSMTPSubmitChannel" + +/* Request a set of auxiliary properties + * conn connection context + * propnames list of auxiliary property names to request ending with + * NULL. + * + * Subsequent calls will add items to the request list. Call with NULL + * to clear the request list. + * + * errors + * SASL_OK -- success + * SASL_BADPARAM -- bad count/conn parameter + * SASL_NOMEM -- out of memory + */ +LIBSASL_API int sasl_auxprop_request(sasl_conn_t *conn, + const char **propnames); + +/* Returns current auxiliary property context. + * Use functions in prop.h to access content + * + * if authentication hasn't completed, property values may be empty/NULL + * + * properties not recognized by active plug-ins will be left empty/NULL + * + * returns NULL if conn is invalid. + */ +LIBSASL_API struct propctx *sasl_auxprop_getctx(sasl_conn_t *conn); + +/* Store the set of auxiliary properties for the given user. + * Use functions in prop.h to set the content. + * + * conn connection context + * ctx property context from prop_new()/prop_request()/prop_set() + * user NUL terminated user + * + * Call with NULL 'ctx' to see if the backend allows storing properties. + * + * errors + * SASL_OK -- success + * SASL_NOMECH -- can not store some/all properties + * SASL_BADPARAM -- bad conn/ctx/user parameter + * SASL_NOMEM -- out of memory + * SASL_FAIL -- failed to store + */ +LIBSASL_API int sasl_auxprop_store(sasl_conn_t *conn, + struct propctx *ctx, const char *user); + +/********************** + * security layer API * + **********************/ + +/* encode a block of data for transmission using security layer, + * returning the input buffer if there is no security layer. + * output is only valid until next call to sasl_encode or sasl_encodev + * returns: + * SASL_OK -- success (returns input if no layer negotiated) + * SASL_NOTDONE -- security layer negotiation not finished + * SASL_BADPARAM -- inputlen is greater than the SASL_MAXOUTBUF + */ +LIBSASL_API int sasl_encode(sasl_conn_t *conn, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen); + +/* encode a block of data for transmission using security layer + * output is only valid until next call to sasl_encode or sasl_encodev + * returns: + * SASL_OK -- success (returns input if no layer negotiated) + * SASL_NOTDONE -- security layer negotiation not finished + * SASL_BADPARAM -- input length is greater than the SASL_MAXOUTBUF + * or no security layer + */ +LIBSASL_API int sasl_encodev(sasl_conn_t *conn, + const struct iovec *invec, unsigned numiov, + const char **output, unsigned *outputlen); + +/* decode a block of data received using security layer + * returning the input buffer if there is no security layer. + * output is only valid until next call to sasl_decode + * + * if outputlen is 0 on return, than the value of output is undefined. + * + * returns: + * SASL_OK -- success (returns input if no layer negotiated) + * SASL_NOTDONE -- security layer negotiation not finished + * SASL_BADMAC -- bad message integrity check + */ +LIBSASL_API int sasl_decode(sasl_conn_t *conn, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen); + +#ifdef __cplusplus +} +#endif + +#endif /* SASL_H */ diff --git a/contrib/libs/sasl/include/saslplug.h b/contrib/libs/sasl/include/saslplug.h new file mode 100644 index 0000000000..ab79e68cf6 --- /dev/null +++ b/contrib/libs/sasl/include/saslplug.h @@ -0,0 +1,986 @@ +/* saslplug.h -- API for SASL plug-ins + */ + +#ifndef SASLPLUG_H +#define SASLPLUG_H 1 + +#ifndef MD5GLOBAL_H +#include "md5global.h" +#endif +#ifndef MD5_H +#include "md5.h" +#endif +#ifndef HMAC_MD5_H +#include "hmac-md5.h" +#endif +#ifndef PROP_H +#include "prop.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* callback to lookup a sasl_callback_t for a connection + * input: + * conn -- the connection to lookup a callback for + * callbacknum -- the number of the callback + * output: + * pproc -- pointer to the callback function (set to NULL on failure) + * pcontext -- pointer to the callback context (set to NULL on failure) + * returns: + * SASL_OK -- no error + * SASL_FAIL -- unable to find a callback of the requested type + * SASL_INTERACT -- caller must use interaction to get data + */ +typedef int (*sasl_callback_ft)(void); +typedef int sasl_getcallback_t(sasl_conn_t *conn, + unsigned long callbackid, + sasl_callback_ft * pproc, + void **pcontext); + +/* The sasl_utils structure will remain backwards compatible unless + * the SASL_*_PLUG_VERSION is changed incompatibly + * higher SASL_UTILS_VERSION numbers indicate more functions are available + */ +#define SASL_UTILS_VERSION 4 + +/* utility function set for plug-ins + */ +typedef struct sasl_utils { + int version; + + /* contexts */ + sasl_conn_t *conn; + sasl_rand_t *rpool; + void *getopt_context; + + /* option function */ + sasl_getopt_t *getopt; + + /* allocation functions: */ + sasl_malloc_t *malloc; + sasl_calloc_t *calloc; + sasl_realloc_t *realloc; + sasl_free_t *free; + + /* mutex functions: */ + sasl_mutex_alloc_t *mutex_alloc; + sasl_mutex_lock_t *mutex_lock; + sasl_mutex_unlock_t *mutex_unlock; + sasl_mutex_free_t *mutex_free; + + /* MD5 hash and HMAC functions */ + void (*MD5Init)(MD5_CTX *); + void (*MD5Update)(MD5_CTX *, const unsigned char *text, unsigned int len); + void (*MD5Final)(unsigned char [16], MD5_CTX *); + void (*hmac_md5)(const unsigned char *text, int text_len, + const unsigned char *key, int key_len, + unsigned char [16]); + void (*hmac_md5_init)(HMAC_MD5_CTX *, const unsigned char *key, int len); + /* hmac_md5_update() is just a call to MD5Update on inner context */ + void (*hmac_md5_final)(unsigned char [16], HMAC_MD5_CTX *); + void (*hmac_md5_precalc)(HMAC_MD5_STATE *, + const unsigned char *key, int len); + void (*hmac_md5_import)(HMAC_MD5_CTX *, HMAC_MD5_STATE *); + + /* mechanism utility functions (same as above): */ + int (*mkchal)(sasl_conn_t *conn, char *buf, unsigned maxlen, + unsigned hostflag); + int (*utf8verify)(const char *str, unsigned len); + void (*rand)(sasl_rand_t *rpool, char *buf, unsigned len); + void (*churn)(sasl_rand_t *rpool, const char *data, unsigned len); + + /* This allows recursive calls to the sasl_checkpass() routine from + * within a SASL plug-in. This MUST NOT be used in the PLAIN mechanism + * as sasl_checkpass MAY be a front-end for the PLAIN mechanism. + * This is intended for use by the non-standard LOGIN mechanism and + * potentially by a future mechanism which uses public-key technology to + * set up a lightweight encryption layer just for sending a password. + */ + int (*checkpass)(sasl_conn_t *conn, + const char *user, unsigned userlen, + const char *pass, unsigned passlen); + + /* Access to base64 encode/decode routines */ + int (*decode64)(const char *in, unsigned inlen, + char *out, unsigned outmax, unsigned *outlen); + int (*encode64)(const char *in, unsigned inlen, + char *out, unsigned outmax, unsigned *outlen); + + /* erase a buffer */ + void (*erasebuffer)(char *buf, unsigned len); + + /* callback to sasl_getprop() and sasl_setprop() */ + int (*getprop)(sasl_conn_t *conn, int propnum, const void **pvalue); + int (*setprop)(sasl_conn_t *conn, int propnum, const void *value); + + /* callback function */ + sasl_getcallback_t *getcallback; + + /* format a message and then pass it to the SASL_CB_LOG callback + * + * use syslog()-style formatting (printf with %m as a human readable text + * (strerror()) for the error specified as the parameter). + * The implementation may use a fixed size buffer not smaller + * than 512 octets if it securely truncates the message. + * + * level is a SASL_LOG_* level (see sasl.h) + */ + void (*log)(sasl_conn_t *conn, int level, const char *fmt, ...) __attribute__((format(printf, 3, 4))); + + /* callback to sasl_seterror() */ + void (*seterror)(sasl_conn_t *conn, unsigned flags, const char *fmt, ...) __attribute__((format(printf, 3, 4))); + + /* spare function pointer */ + int *(*spare_fptr)(void); + + /* auxiliary property utilities */ + struct propctx *(*prop_new)(unsigned estimate); + int (*prop_dup)(struct propctx *src_ctx, struct propctx **dst_ctx); + int (*prop_request)(struct propctx *ctx, const char **names); + const struct propval *(*prop_get)(struct propctx *ctx); + int (*prop_getnames)(struct propctx *ctx, const char **names, + struct propval *vals); + void (*prop_clear)(struct propctx *ctx, int requests); + void (*prop_dispose)(struct propctx **ctx); + int (*prop_format)(struct propctx *ctx, const char *sep, int seplen, + char *outbuf, unsigned outmax, unsigned *outlen); + int (*prop_set)(struct propctx *ctx, const char *name, + const char *value, int vallen); + int (*prop_setvals)(struct propctx *ctx, const char *name, + const char **values); + void (*prop_erase)(struct propctx *ctx, const char *name); + int (*auxprop_store)(sasl_conn_t *conn, + struct propctx *ctx, const char *user); + + /* for additions which don't require a version upgrade; set to 0 */ + int (*spare_fptr1)(void); + int (*spare_fptr2)(void); +} sasl_utils_t; + +/* + * output parameters from SASL API + * + * created / destroyed by the glue code, though probably filled in + * by a combination of the plugin, the glue code, and the canon_user callback. + * + */ +typedef struct sasl_out_params { + unsigned doneflag; /* exchange complete */ + + const char *user; /* canonicalized user name */ + const char *authid; /* canonicalized authentication id */ + + unsigned ulen; /* length of canonicalized user name */ + unsigned alen; /* length of canonicalized authid */ + + /* security layer information */ + unsigned maxoutbuf; /* Maximum buffer size, which will + produce buffer no bigger than the + negotiated SASL maximum buffer size */ + sasl_ssf_t mech_ssf; /* Should be set non-zero if negotiation of a + * security layer was *attempted*, even if + * the negotiation failed */ + void *encode_context; + int (*encode)(void *context, const struct iovec *invec, unsigned numiov, + const char **output, unsigned *outputlen); + void *decode_context; + int (*decode)(void *context, const char *input, unsigned inputlen, + const char **output, unsigned *outputlen); + + /* Pointer to delegated (client's) credentials, if supported by + the SASL mechanism */ + void *client_creds; + + /* for additions which don't require a version upgrade; set to 0 */ + const void *gss_peer_name; + const void *gss_local_name; + const char *cbindingname; /* channel binding name from packet */ + int (*spare_fptr1)(void); + int (*spare_fptr2)(void); + unsigned int cbindingdisp; /* channel binding disposition from client */ + int spare_int2; + int spare_int3; + int spare_int4; + + /* set to 0 initially, this allows a plugin with extended parameters + * to work with an older framework by updating version as parameters + * are added. + */ + int param_version; +} sasl_out_params_t; + + + +/* Used by both client and server side plugins */ +typedef enum { + SASL_INFO_LIST_START = 0, + SASL_INFO_LIST_MECH, + SASL_INFO_LIST_END +} sasl_info_callback_stage_t; + +/****************************** + * Channel binding macros ** + ******************************/ + +typedef enum { + SASL_CB_DISP_NONE = 0, /* client did not support CB */ + SASL_CB_DISP_WANT, /* client supports CB, thinks server does not */ + SASL_CB_DISP_USED /* client supports and used CB */ +} sasl_cbinding_disp_t; + +/* TRUE if channel binding is non-NULL */ +#define SASL_CB_PRESENT(params) ((params)->cbinding != NULL) +/* TRUE if channel binding is marked critical */ +#define SASL_CB_CRITICAL(params) (SASL_CB_PRESENT(params) && \ + (params)->cbinding->critical) + +/****************************** + * Client Mechanism Functions * + ******************************/ + +/* + * input parameters to client SASL plugin + * + * created / destroyed by the glue code + * + */ +typedef struct sasl_client_params { + const char *service; /* service name */ + const char *serverFQDN; /* server fully qualified domain name */ + const char *clientFQDN; /* client's fully qualified domain name */ + const sasl_utils_t *utils; /* SASL API utility routines -- + * for a particular sasl_conn_t, + * MUST remain valid until mech_free is + * called */ + const sasl_callback_t *prompt_supp; /* client callback list */ + const char *iplocalport; /* server IP domain literal & port */ + const char *ipremoteport; /* client IP domain literal & port */ + + unsigned servicelen; /* length of service */ + unsigned slen; /* length of serverFQDN */ + unsigned clen; /* length of clientFQDN */ + unsigned iploclen; /* length of iplocalport */ + unsigned ipremlen; /* length of ipremoteport */ + + /* application's security requirements & info */ + sasl_security_properties_t props; + sasl_ssf_t external_ssf; /* external SSF active */ + + /* for additions which don't require a version upgrade; set to 0 */ + const void *gss_creds; /* GSS credential handle */ + const sasl_channel_binding_t *cbinding; /* client channel binding */ + const sasl_http_request_t *http_request;/* HTTP Digest request method */ + void *spare_ptr4; + + /* Canonicalize a user name from on-wire to internal format + * added rjs3 2001-05-23 + * Must be called once user name aquired if canon_user is non-NULL. + * conn connection context + * in user name from wire protocol (need not be NUL terminated) + * len length of user name from wire protocol (0 = strlen(user)) + * flags for SASL_CU_* flags + * oparams the user, authid, ulen, alen, fields are + * set appropriately after canonicalization/copying and + * authorization of arguments + * + * responsible for setting user, ulen, authid, and alen in the oparams + * structure + * + * default behavior is to strip leading and trailing whitespace, as + * well as allocating space for and copying the parameters. + * + * results: + * SASL_OK -- success + * SASL_NOMEM -- out of memory + * SASL_BADPARAM -- invalid conn + * SASL_BADPROT -- invalid user/authid + */ + int (*canon_user)(sasl_conn_t *conn, + const char *in, unsigned len, + unsigned flags, + sasl_out_params_t *oparams); + + int (*spare_fptr1)(void); + + unsigned int cbindingdisp; + int spare_int2; + int spare_int3; + + /* flags field as passed to sasl_client_new */ + unsigned flags; + + /* set to 0 initially, this allows a plugin with extended parameters + * to work with an older framework by updating version as parameters + * are added. + */ + int param_version; +} sasl_client_params_t; + +/* features shared between client and server */ +/* These allow the glue code to handle client-first and server-last issues */ + +/* This indicates that the mechanism prefers to do client-send-first + * if the protocol allows it. */ +#define SASL_FEAT_WANT_CLIENT_FIRST 0x0002 + +/* This feature is deprecated. Instead, plugins should set *serverout to + * non-NULL and return SASL_OK intelligently to allow flexible use of + * server-last semantics +#define SASL_FEAT_WANT_SERVER_LAST 0x0004 +*/ + +/* This feature is deprecated. Instead, plugins should correctly set + * SASL_FEAT_SERVER_FIRST as needed +#define SASL_FEAT_INTERNAL_CLIENT_FIRST 0x0008 +*/ + +/* This indicates that the plugin is server-first only. + * Not defining either of SASL_FEAT_SERVER_FIRST or + * SASL_FEAT_WANT_CLIENT_FIRST indicates that the mechanism + * will handle the client-first situation internally. + */ +#define SASL_FEAT_SERVER_FIRST 0x0010 + +/* This plugin allows proxying */ +#define SASL_FEAT_ALLOWS_PROXY 0x0020 + +/* server plugin don't use cleartext userPassword attribute */ +#define SASL_FEAT_DONTUSE_USERPASSWD 0x0080 + +/* Underlying mechanism uses GSS framing */ +#define SASL_FEAT_GSS_FRAMING 0x0100 + +/* Underlying mechanism supports channel binding */ +#define SASL_FEAT_CHANNEL_BINDING 0x0800 + +/* This plugin can be used for HTTP authentication */ +#define SASL_FEAT_SUPPORTS_HTTP 0x1000 + +/* client plug-in features */ +#define SASL_FEAT_NEEDSERVERFQDN 0x0001 + +/* a C object for a client mechanism + */ +typedef struct sasl_client_plug { + /* mechanism name */ + const char *mech_name; + + /* best mech additional security layer strength factor */ + sasl_ssf_t max_ssf; + + /* best security flags, as defined in sasl_security_properties_t */ + unsigned security_flags; + + /* features of plugin */ + unsigned features; + + /* required prompt ids, NULL = user/pass only */ + const unsigned long *required_prompts; + + /* global state for mechanism */ + void *glob_context; + + /* create context for mechanism, using params supplied + * glob_context -- from above + * params -- params from sasl_client_new + * conn_context -- context for one connection + * returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + * SASL_WRONGMECH -- mech doesn't support security params + */ + int (*mech_new)(void *glob_context, + sasl_client_params_t *cparams, + void **conn_context); + + /* perform one step of exchange. NULL is passed for serverin on + * first step. + * returns: + * SASL_OK -- success + * SASL_INTERACT -- user interaction needed to fill in prompts + * SASL_BADPROT -- server protocol incorrect/cancelled + * SASL_BADSERV -- server failed mutual auth + */ + int (*mech_step)(void *conn_context, + sasl_client_params_t *cparams, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams); + + /* dispose of connection context from mech_new + */ + void (*mech_dispose)(void *conn_context, const sasl_utils_t *utils); + + /* free all global space used by mechanism + * mech_dispose must be called on all mechanisms first + */ + void (*mech_free)(void *glob_context, const sasl_utils_t *utils); + + /* perform precalculations during a network round-trip + * or idle period. conn_context may be NULL + * returns 1 if action taken, 0 if no action taken + */ + int (*idle)(void *glob_context, + void *conn_context, + sasl_client_params_t *cparams); + + /* for additions which don't require a version upgrade; set to 0 */ + int (*spare_fptr1)(void); + int (*spare_fptr2)(void); +} sasl_client_plug_t; + +#define SASL_CLIENT_PLUG_VERSION 4 + +/* plug-in entry point: + * utils -- utility callback functions + * max_version -- highest client plug version supported + * returns: + * out_version -- client plug version of result + * pluglist -- list of mechanism plug-ins + * plugcount -- number of mechanism plug-ins + * results: + * SASL_OK -- success + * SASL_NOMEM -- failure + * SASL_BADVERS -- max_version too small + * SASL_BADPARAM -- bad config string + * ... + */ +typedef int sasl_client_plug_init_t(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount); + + +/* add a client plug-in + */ +LIBSASL_API int sasl_client_add_plugin(const char *plugname, + sasl_client_plug_init_t *cplugfunc); + +typedef struct client_sasl_mechanism +{ + int version; + + char *plugname; + const sasl_client_plug_t *plug; +} client_sasl_mechanism_t; + +typedef void sasl_client_info_callback_t (client_sasl_mechanism_t *m, + sasl_info_callback_stage_t stage, + void *rock); + +/* Dump information about available client plugins */ +LIBSASL_API int sasl_client_plugin_info (const char *mech_list, + sasl_client_info_callback_t *info_cb, + void *info_cb_rock); + + +/******************** + * Server Functions * + ********************/ + +/* log message formatting routine */ +typedef void sasl_logmsg_p(sasl_conn_t *conn, const char *fmt, ...) __attribute__((format(printf, 2, 3))); + +/* + * input parameters to server SASL plugin + * + * created / destroyed by the glue code + * + */ +typedef struct sasl_server_params { + const char *service; /* NULL = default service for user_exists + and setpass */ + const char *appname; /* name of calling application */ + const char *serverFQDN; /* server default fully qualified domain name + * (e.g., gethostname) */ + const char *user_realm; /* realm for user (NULL = client supplied) */ + const char *iplocalport; /* server IP domain literal & port */ + const char *ipremoteport; /* client IP domain literal & port */ + + unsigned servicelen; /* length of service */ + unsigned applen; /* length of appname */ + unsigned slen; /* length of serverFQDN */ + unsigned urlen; /* length of user_realm */ + unsigned iploclen; /* length of iplocalport */ + unsigned ipremlen; /* length of ipremoteport */ + + /* This indicates the level of logging desired. See SASL_LOG_* + * in sasl.h + * + * Plug-ins can ignore this and just pass their desired level to + * the log callback. This is primarily used to eliminate logging which + * might be a performance problem (e.g., full protocol trace) and + * to select between SASL_LOG_TRACE and SASL_LOG_PASS alternatives + */ + int log_level; + + const sasl_utils_t *utils; /* SASL API utility routines -- + * for a particular sasl_conn_t, + * MUST remain valid until mech_free is + * called */ + const sasl_callback_t *callbacks; /* Callbacks from application */ + + /* application's security requirements */ + sasl_security_properties_t props; + sasl_ssf_t external_ssf; /* external SSF active */ + + /* Pointer to the function which takes the plaintext passphrase and + * transitions a user to non-plaintext mechanisms via setpass calls. + * (NULL = auto transition not enabled/supported) + * + * If passlen is 0, it defaults to strlen(pass). + * returns 0 if no entry added, 1 if entry added + */ + int (*transition)(sasl_conn_t *conn, const char *pass, unsigned passlen); + + /* Canonicalize a user name from on-wire to internal format + * added cjn 1999-09-21 + * Must be called once user name acquired if canon_user is non-NULL. + * conn connection context + * user user name from wire protocol (need not be NUL terminated) + * ulen length of user name from wire protocol (0 = strlen(user)) + * flags for SASL_CU_* flags + * oparams the user, authid, ulen, alen, fields are + * set appropriately after canonicalization/copying and + * authorization of arguments + * + * responsible for setting user, ulen, authid, and alen in the oparams + * structure + * + * default behavior is to strip leading and trailing whitespace, as + * well as allocating space for and copying the parameters. + * + * results: + * SASL_OK -- success + * SASL_NOMEM -- out of memory + * SASL_BADPARAM -- invalid conn + * SASL_BADPROT -- invalid user/authid + */ + int (*canon_user)(sasl_conn_t *conn, + const char *user, unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams); + + /* auxiliary property context (see definitions in prop.h) + * added cjn 2000-01-30 + * + * NOTE: these properties are the ones associated with the + * canonicalized "user" (user to login as / authorization id), not + * the "authid" (user whose credentials are used / authentication id) + * Prefix the property name with a "*" if a property associated with + * the "authid" is interesting. + */ + struct propctx *propctx; + + /* for additions which don't require a version upgrade; set to 0 */ + const void *gss_creds; /* GSS credential handle */ + const sasl_channel_binding_t *cbinding; /* server channel binding */ + const sasl_http_request_t *http_request;/* HTTP Digest request method */ + void *spare_ptr4; + int (*spare_fptr1)(void); + int (*spare_fptr2)(void); + int spare_int1; + int spare_int2; + int spare_int3; + + /* flags field as passed to sasl_server_new */ + unsigned flags; + + /* set to 0 initially, this allows a plugin with extended parameters + * to work with an older framework by updating version as parameters + * are added. + */ + int param_version; +} sasl_server_params_t; + +/* logging levels (more levels may be added later, if necessary): + */ +#define SASL_LOG_NONE 0 /* don't log anything */ +#define SASL_LOG_ERR 1 /* log unusual errors (default) */ +#define SASL_LOG_FAIL 2 /* log all authentication failures */ +#define SASL_LOG_WARN 3 /* log non-fatal warnings */ +#define SASL_LOG_NOTE 4 /* more verbose than LOG_WARN */ +#define SASL_LOG_DEBUG 5 /* more verbose than LOG_NOTE */ +#define SASL_LOG_TRACE 6 /* traces of internal protocols */ +#define SASL_LOG_PASS 7 /* traces of internal protocols, including + * passwords */ + +/* additional flags for setpass() function below: + */ +/* SASL_SET_CREATE create user if pass non-NULL */ +/* SASL_SET_DISABLE disable user */ +#define SASL_SET_REMOVE SASL_SET_CREATE /* remove user if pass is NULL */ + +/* features for server plug-in + */ +#define SASL_FEAT_SERVICE 0x0200 /* service-specific passwords supported */ +#define SASL_FEAT_GETSECRET 0x0400 /* sasl_server_{get,put}secret_t callbacks + * required by plug-in */ + +/* a C object for a server mechanism + */ +typedef struct sasl_server_plug { + /* mechanism name */ + const char *mech_name; + + /* best mech additional security layer strength factor */ + sasl_ssf_t max_ssf; + + /* best security flags, as defined in sasl_security_properties_t */ + unsigned security_flags; + + /* features of plugin */ + unsigned features; + + /* global state for mechanism */ + void *glob_context; + + /* create a new mechanism handler + * glob_context -- global context + * sparams -- server config params + * challenge -- server challenge from previous instance or NULL + * challen -- length of challenge from previous instance or 0 + * out: + * conn_context -- connection context + * errinfo -- error information + * + * returns: + * SASL_OK -- successfully created mech instance + * SASL_* -- any other server error code + */ + int (*mech_new)(void *glob_context, + sasl_server_params_t *sparams, + const char *challenge, + unsigned challen, + void **conn_context); + + /* perform one step in exchange + * + * returns: + * SASL_OK -- success, all done + * SASL_CONTINUE -- success, one more round trip + * SASL_* -- any other server error code + */ + int (*mech_step)(void *conn_context, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams); + + /* dispose of a connection state + */ + void (*mech_dispose)(void *conn_context, const sasl_utils_t *utils); + + /* free global state for mechanism + * mech_dispose must be called on all mechanisms first + */ + void (*mech_free)(void *glob_context, const sasl_utils_t *utils); + + /* set a password (optional) + * glob_context -- global context + * sparams -- service, middleware utilities, etc. props ignored + * user -- user name + * pass -- password/passphrase (NULL = disable/remove/delete) + * passlen -- length of password/passphrase + * oldpass -- old password/passphrase (NULL = transition) + * oldpasslen -- length of password/passphrase + * flags -- see above + * + * returns: + * SASL_NOCHANGE -- no change was needed + * SASL_NOUSER -- no entry for user + * SASL_NOVERIFY -- no mechanism compatible entry for user + * SASL_PWLOCK -- password locked + * SASL_DIABLED -- account disabled + * etc. + */ + int (*setpass)(void *glob_context, + sasl_server_params_t *sparams, + const char *user, + const char *pass, unsigned passlen, + const char *oldpass, unsigned oldpasslen, + unsigned flags); + + /* query which mechanisms are available for user + * glob_context -- context + * sparams -- service, middleware utilities, etc. props ignored + * user -- NUL terminated user name + * maxmech -- max number of strings in mechlist (0 = no output) + * output: + * mechlist -- an array of C string pointers, filled in with + * mechanism names available to the user + * + * returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + * SASL_FAIL -- lower level failure + * SASL_DISABLED -- account disabled + * SASL_NOUSER -- user not found + * SASL_BUFOVER -- maxmech is too small + * SASL_NOVERIFY -- user found, but no mechanisms available + */ + int (*user_query)(void *glob_context, + sasl_server_params_t *sparams, + const char *user, + int maxmech, + const char **mechlist); + + /* perform precalculations during a network round-trip + * or idle period. conn_context may be NULL (optional) + * returns 1 if action taken, 0 if no action taken + */ + int (*idle)(void *glob_context, + void *conn_context, + sasl_server_params_t *sparams); + + /* check if mechanism is available + * optional--if NULL, mechanism is available based on ENABLE= in config + * + * If this routine sets conn_context to a non-NULL value, then the call + * to mech_new will be skipped. This should not be done unless + * there's a significant performance benefit, since it can cause + * additional memory allocation in SASL core code to keep track of + * contexts potentially for multiple mechanisms. + * + * This is called by the first call to sasl_listmech() for a + * given connection context, thus for a given protocol it may + * never be called. Note that if mech_avail returns SASL_NOMECH, + * then that mechanism is considered disabled for the remainder + * of the session. If mech_avail returns SASL_NOTDONE, then a + * future call to mech_avail may still return either SASL_OK + * or SASL_NOMECH. + * + * returns SASL_OK on success, + * SASL_NOTDONE if mech is not available now, but may be later + * (e.g. EXTERNAL w/o auth_id) + * SASL_NOMECH if mech disabled + */ + int (*mech_avail)(void *glob_context, + sasl_server_params_t *sparams, + void **conn_context); + + /* for additions which don't require a version upgrade; set to 0 */ + int (*spare_fptr2)(void); +} sasl_server_plug_t; + +#define SASL_SERVER_PLUG_VERSION 4 + +/* plug-in entry point: + * utils -- utility callback functions + * plugname -- name of plug-in (may be NULL) + * max_version -- highest server plug version supported + * returns: + * out_version -- server plug-in version of result + * pluglist -- list of mechanism plug-ins + * plugcount -- number of mechanism plug-ins + * results: + * SASL_OK -- success + * SASL_NOMEM -- failure + * SASL_BADVERS -- max_version too small + * SASL_BADPARAM -- bad config string + * ... + */ +typedef int sasl_server_plug_init_t(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount); + +/* + * add a server plug-in + */ +LIBSASL_API int sasl_server_add_plugin(const char *plugname, + sasl_server_plug_init_t *splugfunc); + + +typedef struct server_sasl_mechanism +{ + int version; + int condition; /* set to SASL_NOUSER if no available users; + set to SASL_CONTINUE if delayed plugin loading */ + char *plugname; /* for AUTHSOURCE tracking */ + const sasl_server_plug_t *plug; + char *f; /* where should i load the mechanism from? */ +} server_sasl_mechanism_t; + +typedef void sasl_server_info_callback_t (server_sasl_mechanism_t *m, + sasl_info_callback_stage_t stage, + void *rock); + + +/* Dump information about available server plugins (separate functions are + used for canon and auxprop plugins) */ +LIBSASL_API int sasl_server_plugin_info (const char *mech_list, + sasl_server_info_callback_t *info_cb, + void *info_cb_rock); + + +/********************************************************* + * user canonicalization plug-in -- added cjn 1999-09-29 * + *********************************************************/ + +typedef struct sasl_canonuser { + /* optional features of plugin (set to 0) */ + int features; + + /* spare integer (set to 0) */ + int spare_int1; + + /* global state for plugin */ + void *glob_context; + + /* name of plugin */ + char *name; + + /* free global state for plugin */ + void (*canon_user_free)(void *glob_context, const sasl_utils_t *utils); + + /* canonicalize a username + * glob_context -- global context from this structure + * sparams -- server params, note user_realm&propctx elements + * user -- user to login as (may not be NUL terminated) + * len -- length of user name (0 = strlen(user)) + * flags -- for SASL_CU_* flags + * out -- buffer to copy user name + * out_max -- max length of user name + * out_len -- set to length of user name + * + * note that the output buffers MAY be the same as the input buffers. + * + * returns + * SASL_OK on success + * SASL_BADPROT username contains invalid character + */ + int (*canon_user_server)(void *glob_context, + sasl_server_params_t *sparams, + const char *user, unsigned len, + unsigned flags, + char *out, + unsigned out_umax, unsigned *out_ulen); + + int (*canon_user_client)(void *glob_context, + sasl_client_params_t *cparams, + const char *user, unsigned len, + unsigned flags, + char *out, + unsigned out_max, unsigned *out_len); + + /* for additions which don't require a version upgrade; set to 0 */ + int (*spare_fptr1)(void); + int (*spare_fptr2)(void); + int (*spare_fptr3)(void); +} sasl_canonuser_plug_t; + +#define SASL_CANONUSER_PLUG_VERSION 5 + +/* default name for canonuser plug-in entry point is "sasl_canonuser_init" + * similar to sasl_server_plug_init model, except only returns one + * sasl_canonuser_plug_t structure; + */ +typedef int sasl_canonuser_init_t(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_canonuser_plug_t **plug, + const char *plugname); + +/* add a canonuser plugin + */ +LIBSASL_API int sasl_canonuser_add_plugin(const char *plugname, + sasl_canonuser_init_t *canonuserfunc); + +/****************************************************** + * auxiliary property plug-in -- added cjn 1999-09-29 * + ******************************************************/ + +typedef struct sasl_auxprop_plug { + /* optional features of plugin (none defined yet, set to 0) */ + int features; + + /* spare integer, must be set to 0 */ + int spare_int1; + + /* global state for plugin */ + void *glob_context; + + /* free global state for plugin (OPTIONAL) */ + void (*auxprop_free)(void *glob_context, const sasl_utils_t *utils); + + /* fill in fields of an auxiliary property context + * last element in array has id of SASL_AUX_END + * elements with non-0 len should be ignored. + */ + int (*auxprop_lookup)(void *glob_context, + sasl_server_params_t *sparams, + unsigned flags, + const char *user, unsigned ulen); + + /* name of the auxprop plugin */ + char *name; + + /* store the fields/values of an auxiliary property context (OPTIONAL) + * + * if ctx is NULL, just check if storing properties is enabled + * + * returns + * SASL_OK on success + * SASL_FAIL on failure + */ + int (*auxprop_store)(void *glob_context, + sasl_server_params_t *sparams, + struct propctx *ctx, + const char *user, unsigned ulen); +} sasl_auxprop_plug_t; + +/* auxprop lookup flags */ +#define SASL_AUXPROP_OVERRIDE 0x01 /* if clear, ignore auxiliary properties + * with non-zero len field. If set, + * override value of those properties */ +#define SASL_AUXPROP_AUTHZID 0x02 /* if clear, we are looking up the + * authid flags (prefixed with *), otherwise + * we are looking up the authzid flags + * (no prefix) */ + +/* NOTE: Keep in sync with SASL_CU_<XXX> flags */ +#define SASL_AUXPROP_VERIFY_AGAINST_HASH 0x10 + + +#define SASL_AUXPROP_PLUG_VERSION 8 + +/* default name for auxprop plug-in entry point is "sasl_auxprop_init" + * similar to sasl_server_plug_init model, except only returns one + * sasl_auxprop_plug_t structure; + */ +typedef int sasl_auxprop_init_t(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_auxprop_plug_t **plug, + const char *plugname); + +/* add an auxiliary property plug-in + */ +LIBSASL_API int sasl_auxprop_add_plugin(const char *plugname, + sasl_auxprop_init_t *auxpropfunc); + +typedef void auxprop_info_callback_t (sasl_auxprop_plug_t *m, + sasl_info_callback_stage_t stage, + void *rock); + +/* Dump information about available auxprop plugins (separate functions are + used for canon and server authentication plugins) */ +LIBSASL_API int auxprop_plugin_info (const char *mech_list, + auxprop_info_callback_t *info_cb, + void *info_cb_rock); + +#ifdef __cplusplus +} +#endif + +#endif /* SASLPLUG_H */ diff --git a/contrib/libs/sasl/include/saslutil.h b/contrib/libs/sasl/include/saslutil.h new file mode 100644 index 0000000000..e0fa47c591 --- /dev/null +++ b/contrib/libs/sasl/include/saslutil.h @@ -0,0 +1,99 @@ +/* saslutil.h -- various utility functions in SASL library + */ + +#ifndef SASLUTIL_H +#define SASLUTIL_H 1 + +#ifndef SASL_H +#include "sasl.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* base64 decode + * in -- input data + * inlen -- length of input data + * out -- output data (may be same as in, must have enough space) + * outmax -- max size of output buffer + * result: + * outlen -- actual output length + * + * returns SASL_BADPROT on bad base64, + * SASL_BUFOVER if result won't fit + * SASL_OK on success + */ +LIBSASL_API int sasl_decode64(const char *in, unsigned inlen, + char *out, unsigned outmax, unsigned *outlen); + +/* base64 encode + * in -- input data + * inlen -- input data length + * out -- output buffer (will be NUL terminated) + * outmax -- max size of output buffer + * result: + * outlen -- gets actual length of output buffer (optional) + * + * Returns SASL_OK on success, SASL_BUFOVER if result won't fit + */ +LIBSASL_API int sasl_encode64(const char *in, unsigned inlen, + char *out, unsigned outmax, unsigned *outlen); + +/* make a challenge string (NUL terminated) + * buf -- buffer for result + * maxlen -- max length of result + * hostflag -- 0 = don't include hostname, 1 = include hostname + * returns final length or 0 if not enough space + */ +LIBSASL_API int sasl_mkchal(sasl_conn_t *conn, char *buf, + unsigned maxlen, unsigned hostflag); + +/* verify a string is valid UTF-8 + * if len == 0, strlen(str) will be used. + * returns SASL_BADPROT on error, SASL_OK on success + */ +LIBSASL_API int sasl_utf8verify(const char *str, unsigned len); + +/* create random pool seeded with OS-based params */ +LIBSASL_API int sasl_randcreate(sasl_rand_t **rpool); + +/* free random pool from randcreate */ +LIBSASL_API void sasl_randfree(sasl_rand_t **rpool); + +/* seed random number generator */ +LIBSASL_API void sasl_randseed(sasl_rand_t *rpool, const char *seed, + unsigned len); + +/* generate random octets */ +LIBSASL_API void sasl_rand(sasl_rand_t *rpool, char *buf, unsigned len); + +/* churn data into random number generator */ +LIBSASL_API void sasl_churn(sasl_rand_t *rpool, const char *data, + unsigned len); + +/* erase a security sensitive buffer or password. + * Implementation may use recovery-resistant erase logic. + */ +LIBSASL_API void sasl_erasebuffer(char *pass, unsigned len); + +/* Lowercase string in place */ +LIBSASL_API char *sasl_strlower (char *val); + +LIBSASL_API int sasl_config_init(const char *filename); + +LIBSASL_API void sasl_config_done(void); + +#ifdef WIN32 +/* Just in case a different DLL defines this as well */ +#if defined(NEED_GETOPT) +LIBSASL_API int getopt(int argc, char **argv, char *optstring); +#endif +LIBSASL_API char * getpass(const char *prompt); +#endif /* WIN32 */ + +#ifdef __cplusplus +} +#endif + +#endif /* SASLUTIL_H */ diff --git a/contrib/libs/sasl/lib/auxprop.c b/contrib/libs/sasl/lib/auxprop.c new file mode 100644 index 0000000000..1b0162db74 --- /dev/null +++ b/contrib/libs/sasl/lib/auxprop.c @@ -0,0 +1,1202 @@ +/* auxprop.c - auxilliary property support + * Rob Siemborski + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <sasl.h> +#include <prop.h> +#include <ctype.h> +#include <stdio.h> +#include "saslint.h" + +struct proppool +{ + struct proppool *next; + + size_t size; /* Size of Block */ + size_t unused; /* Space unused in this pool between end + * of char** area and beginning of char* area */ + + char data[1]; /* Variable Sized */ +}; + +struct propctx { + struct propval *values; + struct propval *prev_val; /* Previous value used by set/setvalues */ + + unsigned used_values, allocated_values; + + char *data_end; /* Bottom of string area in current pool */ + char **list_end; /* Top of list area in current pool */ + + struct proppool *mem_base; + struct proppool *mem_cur; +}; + +typedef struct auxprop_plug_list +{ + struct auxprop_plug_list *next; + const sasl_auxprop_plug_t *plug; +} auxprop_plug_list_t; + +static auxprop_plug_list_t *auxprop_head = NULL; + +static struct proppool *alloc_proppool(size_t size) +{ + struct proppool *ret; + /* minus 1 for the one that is already a part of the array + * in the struct */ + size_t total_size = sizeof(struct proppool) + size - 1; + ret = sasl_ALLOC(total_size); + if(!ret) return NULL; + + memset(ret, 0, total_size); + + ret->size = ret->unused = size; + + return ret; +} + +/* Resize a proppool. Invalidates the unused value for this pool */ +static struct proppool *resize_proppool(struct proppool *pool, size_t size) +{ + struct proppool *ret; + + if(pool->size >= size) return pool; + ret = sasl_REALLOC(pool, sizeof(struct proppool) + size); + if(!ret) return NULL; + + ret->size = size; + + return ret; +} + +static int prop_init(struct propctx *ctx, unsigned estimate) +{ + const unsigned VALUES_SIZE = PROP_DEFAULT * sizeof(struct propval); + + ctx->mem_base = alloc_proppool(VALUES_SIZE + estimate); + if(!ctx->mem_base) return SASL_NOMEM; + + ctx->mem_cur = ctx->mem_base; + + ctx->values = (struct propval *)ctx->mem_base->data; + ctx->mem_base->unused = ctx->mem_base->size - VALUES_SIZE; + ctx->allocated_values = PROP_DEFAULT; + ctx->used_values = 0; + + ctx->data_end = ctx->mem_base->data + ctx->mem_base->size; + ctx->list_end = (char **)(ctx->mem_base->data + VALUES_SIZE); + + ctx->prev_val = NULL; + + return SASL_OK; +} + +/* create a property context + * estimate -- an estimate of the storage needed for requests & responses + * 0 will use module default + * returns NULL on error + */ +struct propctx *prop_new(unsigned estimate) +{ + struct propctx *new_ctx; + + if(!estimate) estimate = PROP_DEFAULT * 255; + + new_ctx = sasl_ALLOC(sizeof(struct propctx)); + if(!new_ctx) return NULL; + + if(prop_init(new_ctx, estimate) != SASL_OK) { + prop_dispose(&new_ctx); + } + + return new_ctx; +} + +/* create new propctx which duplicates the contents of an existing propctx + * returns -1 on error + */ +int prop_dup(struct propctx *src_ctx, struct propctx **dst_ctx) +{ + struct proppool *pool; + struct propctx *retval = NULL; + unsigned i; + int result; + unsigned total_size = 0; + size_t values_size; + + if(!src_ctx || !dst_ctx) return SASL_BADPARAM; + + /* What is the total allocated size of src_ctx? */ + pool = src_ctx->mem_base; + while(pool) { + total_size += (unsigned) pool->size; + pool = pool->next; + } + + /* allocate the new context */ + retval = prop_new(total_size); + if(!retval) return SASL_NOMEM; + + retval->used_values = src_ctx->used_values; + retval->allocated_values = src_ctx->used_values + 1; + + values_size = (retval->allocated_values * sizeof(struct propval)); + + retval->mem_base->unused = retval->mem_base->size - values_size; + + retval->list_end = (char **)(retval->mem_base->data + values_size); + /* data_end should still be OK */ + + /* Now dup the values */ + for(i=0; i<src_ctx->used_values; i++) { + retval->values[i].name = src_ctx->values[i].name; + result = prop_setvals(retval, retval->values[i].name, + src_ctx->values[i].values); + if(result != SASL_OK) + goto fail; + } + + retval->prev_val = src_ctx->prev_val; + + *dst_ctx = retval; + return SASL_OK; + + fail: + if(retval) prop_dispose(&retval); + return result; +} + +/* + * dispose of property context + * ctx -- is disposed and set to NULL; noop if ctx or *ctx is NULL + */ +void prop_dispose(struct propctx **ctx) +{ + struct proppool *tmp; + + if(!ctx || !*ctx) return; + + while((*ctx)->mem_base) { + tmp = (*ctx)->mem_base; + (*ctx)->mem_base = tmp->next; + sasl_FREE(tmp); + } + + sasl_FREE(*ctx); + *ctx = NULL; + + return; +} + +/* Add property names to request + * ctx -- context from prop_new() + * names -- list of property names; must persist until context freed + * or requests cleared + * + * NOTE: may clear values from context as side-effect + * returns -1 on error + */ +int prop_request(struct propctx *ctx, const char **names) +{ + unsigned i, new_values, total_values; + + if(!ctx || !names) return SASL_BADPARAM; + + /* Count how many we need to add */ + for(new_values=0; names[new_values]; new_values++); + + /* Do we need to add ANY? */ + if(!new_values) return SASL_OK; + + /* We always want at least one extra to mark the end of the array */ + total_values = new_values + ctx->used_values + 1; + + /* Do we need to increase the size of our propval table? */ + if(total_values > ctx->allocated_values) { + unsigned max_in_pool; + + /* Do we need a larger base pool? */ + max_in_pool = (unsigned) (ctx->mem_base->size / sizeof(struct propval)); + + if(total_values <= max_in_pool) { + /* Don't increase the size of the base pool, just use what + we need */ + ctx->allocated_values = total_values; + ctx->mem_base->unused = + ctx->mem_base->size - (sizeof(struct propval) + * ctx->allocated_values); + } else { + /* We need to allocate more! */ + unsigned new_alloc_length; + size_t new_size; + + new_alloc_length = 2 * ctx->allocated_values; + while(total_values > new_alloc_length) { + new_alloc_length *= 2; + } + + new_size = new_alloc_length * sizeof(struct propval); + ctx->mem_base = resize_proppool(ctx->mem_base, new_size); + + if(!ctx->mem_base) { + ctx->values = NULL; + ctx->allocated_values = ctx->used_values = 0; + return SASL_NOMEM; + } + + /* It worked! Update the structure! */ + ctx->values = (struct propval *)ctx->mem_base->data; + ctx->allocated_values = new_alloc_length; + ctx->mem_base->unused = ctx->mem_base->size + - sizeof(struct propval) * ctx->allocated_values; + } + + /* Clear out new propvals */ + memset(&(ctx->values[ctx->used_values]), 0, + sizeof(struct propval) * (ctx->allocated_values - ctx->used_values)); + + /* Finish updating the context -- we've extended the list! */ + /* ctx->list_end = (char **)(ctx->values + ctx->allocated_values); */ + /* xxx test here */ + ctx->list_end = (char **)(ctx->values + total_values); + } + + /* Now do the copy, or referencing rather */ + for(i=0;i<new_values;i++) { + unsigned j, flag; + + flag = 0; + + /* Check for dups */ + for(j=0;j<ctx->used_values;j++) { + if(!strcmp(ctx->values[j].name, names[i])) { + flag = 1; + break; + } + } + + /* We already have it... skip! */ + if(flag) continue; + + ctx->values[ctx->used_values++].name = names[i]; + } + + prop_clear(ctx, 0); + + return SASL_OK; +} + +/* return array of struct propval from the context + * return value persists until next call to + * prop_request, prop_clear or prop_dispose on context + */ +const struct propval *prop_get(struct propctx *ctx) +{ + if(!ctx) return NULL; + + return ctx->values; +} + +/* Fill in an array of struct propval based on a list of property names + * return value persists until next call to + * prop_request, prop_clear or prop_dispose on context + * returns -1 on error (no properties ever requested, ctx NULL, etc) + * returns number of matching properties which were found (values != NULL) + * if a name requested here was never requested by a prop_request, then + * the name field of the associated vals entry will be set to NULL + */ +int prop_getnames(struct propctx *ctx, const char **names, + struct propval *vals) +{ + int found_names = 0; + + struct propval *cur = vals; + const char **curname; + + if(!ctx || !names || !vals) return SASL_BADPARAM; + + for(curname = names; *curname; curname++) { + struct propval *val; + for(val = ctx->values; val->name; val++) { + if(!strcmp(*curname,val->name)) { + found_names++; + memcpy(cur, val, sizeof(struct propval)); + goto next; + } + } + + /* If we are here, we didn't find it */ + memset(cur, 0, sizeof(struct propval)); + + next: + cur++; + } + + return found_names; +} + + +/* clear values and optionally requests from property context + * ctx -- property context + * requests -- 0 = don't clear requests, 1 = clear requests + */ +void prop_clear(struct propctx *ctx, int requests) +{ + struct proppool *new_pool, *tmp; + unsigned i; + + /* We're going to need a new proppool once we reset things */ + new_pool = alloc_proppool(ctx->mem_base->size + + (ctx->used_values+1) * sizeof(struct propval)); + if (new_pool == NULL) { + _sasl_log(NULL, SASL_LOG_ERR, "failed to allocate memory\n"); + exit(1); + } + + if(requests) { + /* We're wiping the whole shebang */ + ctx->used_values = 0; + } else { + /* Need to keep around old requets */ + struct propval *new_values = (struct propval *)new_pool->data; + for(i=0; i<ctx->used_values; i++) { + new_values[i].name = ctx->values[i].name; + } + } + + while(ctx->mem_base) { + tmp = ctx->mem_base; + ctx->mem_base = tmp->next; + sasl_FREE(tmp); + } + + /* Update allocation-related metadata */ + ctx->allocated_values = ctx->used_values+1; + new_pool->unused = + new_pool->size - (ctx->allocated_values * sizeof(struct propval)); + + /* Setup pointers for the values array */ + ctx->values = (struct propval *)new_pool->data; + ctx->prev_val = NULL; + + /* Setup the pools */ + ctx->mem_base = ctx->mem_cur = new_pool; + + /* Reset list_end and data_end for the new memory pool */ + ctx->list_end = + (char **)((char *)ctx->mem_base->data + ctx->allocated_values * sizeof(struct propval)); + ctx->data_end = (char *)ctx->mem_base->data + ctx->mem_base->size; + + return; +} + +/* + * erase the value of a property + */ +void prop_erase(struct propctx *ctx, const char *name) +{ + struct propval *val; + int i; + + if(!ctx || !name) return; + + for(val = ctx->values; val->name; val++) { + if(!strcmp(name,val->name)) { + if(!val->values) break; + + /* + * Yes, this is casting away the const, but + * we should be okay because the only place this + * memory should be is in the proppool's + */ + for(i=0;val->values[i];i++) { + memset((void *)(val->values[i]),0,strlen(val->values[i])); + val->values[i] = NULL; + } + + val->values = NULL; + val->nvalues = 0; + val->valsize = 0; + break; + } + } + + return; +} + +/****fetcher interfaces****/ + +/* format the requested property names into a string + * ctx -- context from prop_new()/prop_request() + * sep -- separator between property names (unused if none requested) + * seplen -- length of separator, if < 0 then strlen(sep) will be used + * outbuf -- output buffer + * outmax -- maximum length of output buffer including NUL terminator + * outlen -- set to length of output string excluding NUL terminator + * returns 0 on success and amount of additional space needed on failure + */ +int prop_format(struct propctx *ctx, const char *sep, int seplen, + char *outbuf, unsigned outmax, unsigned *outlen) +{ + unsigned needed, flag = 0; + struct propval *val; + + if (!ctx || !outbuf) return SASL_BADPARAM; + + if (!sep) seplen = 0; + if (seplen < 0) seplen = (int) strlen(sep); +/* If seplen is negative now we have overflow. + But if you have a string longer than 2Gb, you are an idiot anyway */ + if (seplen < 0) return SASL_BADPARAM; + + needed = seplen * (ctx->used_values - 1); + for(val = ctx->values; val->name; val++) { + needed += (unsigned) strlen(val->name); + } + + if(!outmax) return (needed + 1); /* Because of unsigned funkiness */ + if(needed > (outmax - 1)) return (needed - (outmax - 1)); + + *outbuf = '\0'; + if(outlen) *outlen = needed; + + if(needed == 0) return SASL_OK; + + for(val = ctx->values; val->name; val++) { + if(seplen && flag) { + strncat(outbuf, sep, seplen); + } else { + flag = 1; + } + strcat(outbuf, val->name); + } + + return SASL_OK; +} + +/* add a property value to the context + * ctx -- context from prop_new()/prop_request() + * name -- name of property to which value will be added + * if NULL, add to the same name as previous prop_set/setvals call + * value -- a value for the property; will be copied into context + * if NULL, remove existing values + * vallen -- length of value, if <= 0 then strlen(value) will be used + */ +int prop_set(struct propctx *ctx, const char *name, + const char *value, int vallen) +{ + struct propval *cur; + + if(!ctx) return SASL_BADPARAM; + if(!name && !ctx->prev_val) return SASL_BADPARAM; + + if(name) { + struct propval *val; + + ctx->prev_val = NULL; + + for(val = ctx->values; val->name; val++) { + if(!strcmp(name,val->name)){ + ctx->prev_val = val; + break; + } + } + + /* Couldn't find it! */ + if(!ctx->prev_val) return SASL_BADPARAM; + } + + cur = ctx->prev_val; + + if(name) /* New Entry */ { + unsigned nvalues = 1; /* 1 for NULL entry */ + const char **old_values = NULL; + char **tmp, **tmp2; + size_t size; + + if(cur->values) { + + if(!value) { + /* If we would be adding a null value, then we are done */ + return SASL_OK; + } + + old_values = cur->values; + tmp = (char **)cur->values; + while(*tmp) { + nvalues++; + tmp++; + } + + } + + if(value) { + nvalues++; /* for the new value */ + } + + size = nvalues * sizeof(char*); + + if(size > ctx->mem_cur->unused) { + size_t needed; + + for(needed = ctx->mem_cur->size * 2; needed < size; needed *= 2); + + /* Allocate a new proppool */ + ctx->mem_cur->next = alloc_proppool(needed); + if(!ctx->mem_cur->next) return SASL_NOMEM; + + ctx->mem_cur = ctx->mem_cur->next; + + ctx->list_end = (char **)ctx->mem_cur->data; + ctx->data_end = ctx->mem_cur->data + needed; + } + + /* Grab the memory */ + ctx->mem_cur->unused -= size; + cur->values = (const char **)ctx->list_end; + cur->values[nvalues - 1] = NULL; + + /* Finish updating the context */ + ctx->list_end = (char **)(cur->values + nvalues); + + /* If we don't have an actual value to fill in, we are done */ + if(!value) + return SASL_OK; + + tmp2 = (char **)cur->values; + if(old_values) { + tmp = (char **)old_values; + + while(*tmp) { + *tmp2 = *tmp; + tmp++; tmp2++; + } + } + + /* Now allocate the last entry */ + if(vallen <= 0) + size = (size_t)(strlen(value) + 1); + else + size = (size_t)(vallen + 1); + + if(size > ctx->mem_cur->unused) { + size_t needed; + + needed = ctx->mem_cur->size * 2; + + while(needed < size) { + needed *= 2; + } + + /* Allocate a new proppool */ + ctx->mem_cur->next = alloc_proppool(needed); + if(!ctx->mem_cur->next) return SASL_NOMEM; + + ctx->mem_cur = ctx->mem_cur->next; + ctx->list_end = (char **)ctx->mem_cur->data; + ctx->data_end = ctx->mem_cur->data + needed; + } + + /* Update the data_end pointer */ + ctx->data_end -= size; + ctx->mem_cur->unused -= size; + + /* Copy and setup the new value! */ + memcpy(ctx->data_end, value, size-1); + ctx->data_end[size - 1] = '\0'; + cur->values[nvalues - 2] = ctx->data_end; + + cur->nvalues++; + cur->valsize += ((unsigned) size - 1); + } else /* Appending an entry */ { + char **tmp; + size_t size; + + /* If we are setting it to be NULL, we are done */ + if(!value) return SASL_OK; + + size = sizeof(char*); + + /* Is it in the current pool, and will it fit in the unused space? */ + if(size > ctx->mem_cur->unused && + (void *)cur->values > (void *)(ctx->mem_cur->data) && + (void *)cur->values < (void *)(ctx->mem_cur->data + ctx->mem_cur->size)) { + /* recursively call the not-fast way */ + return prop_set(ctx, cur->name, value, vallen); + } + + /* Note the invariant: the previous value list must be + at the top of the CURRENT pool at this point */ + + /* Grab the memory */ + ctx->mem_cur->unused -= size; + ctx->list_end++; + + *(ctx->list_end - 1) = NULL; + tmp = (ctx->list_end - 2); + + /* Now allocate the last entry */ + if(vallen <= 0) + size = strlen(value) + 1; + else + size = vallen + 1; + + if(size > ctx->mem_cur->unused) { + size_t needed; + + needed = ctx->mem_cur->size * 2; + + while(needed < size) { + needed *= 2; + } + + /* Allocate a new proppool */ + ctx->mem_cur->next = alloc_proppool(needed); + if(!ctx->mem_cur->next) return SASL_NOMEM; + + ctx->mem_cur = ctx->mem_cur->next; + ctx->list_end = (char **)ctx->mem_cur->data; + ctx->data_end = ctx->mem_cur->data + needed; + } + + /* Update the data_end pointer */ + ctx->data_end -= size; + ctx->mem_cur->unused -= size; + + /* Copy and setup the new value! */ + memcpy(ctx->data_end, value, size-1); + ctx->data_end[size - 1] = '\0'; + *tmp = ctx->data_end; + + cur->nvalues++; + cur->valsize += ((unsigned) size - 1); + } + + return SASL_OK; +} + + +/* set the values for a property + * ctx -- context from prop_new()/prop_request() + * name -- name of property to which value will be added + * if NULL, add to the same name as previous prop_set/setvals call + * values -- array of values, ending in NULL. Each value is a NUL terminated + * string + */ +int prop_setvals(struct propctx *ctx, const char *name, + const char **values) +{ + const char **val = values; + int result = SASL_OK; + + if(!ctx) return SASL_BADPARAM; + + /* If they want us to add no values, we can do that */ + if(!values) return SASL_OK; + + /* Basically, use prop_set to do all our dirty work for us */ + if(name) { + result = prop_set(ctx, name, *val, 0); + val++; + } + + for(;*val;val++) { + if(result != SASL_OK) return result; + result = prop_set(ctx, NULL, *val,0); + } + + return result; +} + +/* Request a set of auxiliary properties + * conn connection context + * propnames list of auxiliary property names to request ending with + * NULL. + * + * Subsequent calls will add items to the request list. Call with NULL + * to clear the request list. + * + * errors + * SASL_OK -- success + * SASL_BADPARAM -- bad count/conn parameter + * SASL_NOMEM -- out of memory + */ +int sasl_auxprop_request(sasl_conn_t *conn, const char **propnames) +{ + int result; + sasl_server_conn_t *sconn; + + if(!conn) return SASL_BADPARAM; + if(conn->type != SASL_CONN_SERVER) + PARAMERROR(conn); + + sconn = (sasl_server_conn_t *)conn; + + if(!propnames) { + prop_clear(sconn->sparams->propctx,1); + return SASL_OK; + } + + result = prop_request(sconn->sparams->propctx, propnames); + RETURN(conn, result); +} + + +/* Returns current auxiliary property context. + * Use functions in prop.h to access content + * + * if authentication hasn't completed, property values may be empty/NULL + * + * properties not recognized by active plug-ins will be left empty/NULL + * + * returns NULL if conn is invalid. + */ +struct propctx *sasl_auxprop_getctx(sasl_conn_t *conn) +{ + sasl_server_conn_t *sconn; + + if(!conn || conn->type != SASL_CONN_SERVER) return NULL; + + sconn = (sasl_server_conn_t *)conn; + + return sconn->sparams->propctx; +} + +/* add an auxiliary property plugin */ +int sasl_auxprop_add_plugin(const char *plugname, + sasl_auxprop_init_t *auxpropfunc) +{ + int result, out_version; + auxprop_plug_list_t *new_item; + sasl_auxprop_plug_t *plug; + + result = auxpropfunc(sasl_global_utils, SASL_AUXPROP_PLUG_VERSION, + &out_version, &plug, plugname); + + /* Check if out_version is too old. + We only support the current at the moment */ + if (result == SASL_OK && out_version < SASL_AUXPROP_PLUG_VERSION) { + result = SASL_BADVERS; + } + + if(result != SASL_OK) { + _sasl_log(NULL, SASL_LOG_ERR, "auxpropfunc error %s\n", + sasl_errstring(result, NULL, NULL)); + return result; + } + + /* We require that this function is implemented */ + if(!plug->auxprop_lookup) return SASL_BADPROT; + + new_item = sasl_ALLOC(sizeof(auxprop_plug_list_t)); + if(!new_item) return SASL_NOMEM; + + /* These will load from least-important to most important */ + new_item->plug = plug; + new_item->next = auxprop_head; + auxprop_head = new_item; + + return SASL_OK; +} + +void _sasl_auxprop_free() +{ + auxprop_plug_list_t *ptr, *ptr_next; + + for(ptr = auxprop_head; ptr; ptr = ptr_next) { + ptr_next = ptr->next; + if(ptr->plug->auxprop_free) + ptr->plug->auxprop_free(ptr->plug->glob_context, + sasl_global_utils); + sasl_FREE(ptr); + } + + auxprop_head = NULL; +} + +/* Return the updated account status based on the current ("so far") and + the specific status returned by the latest auxprop call */ +static int +_sasl_account_status (int current_status, + int specific_status) +{ + switch (specific_status) { + case SASL_NOVERIFY: + specific_status = SASL_OK; + /* fall through */ + case SASL_OK: + if (current_status == SASL_NOMECH || + current_status == SASL_NOUSER) { + current_status = specific_status; + } + break; + + case SASL_NOUSER: + if (current_status == SASL_NOMECH) { + current_status = specific_status; + } + break; + + /* NOTE: The disabled flag sticks, unless we hit an error */ + case SASL_DISABLED: + if (current_status == SASL_NOMECH || + current_status == SASL_NOUSER || + current_status == SASL_OK) { + current_status = specific_status; + } + break; + + case SASL_NOMECH: + /* ignore */ + break; + + /* SASL_UNAVAIL overrides everything */ + case SASL_UNAVAIL: + current_status = specific_status; + break; + + default: + current_status = specific_status; + break; + } + return (current_status); +} + +/* Do the callbacks for auxprop lookups */ +int _sasl_auxprop_lookup(sasl_server_params_t *sparams, + unsigned flags, + const char *user, unsigned ulen) +{ + sasl_getopt_t *getopt; + int ret, found = 0; + void *context; + const char *plist = NULL; + auxprop_plug_list_t *ptr; + int result = SASL_NOMECH; + + if(_sasl_getcallback(sparams->utils->conn, + SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, + &context) == SASL_OK) { + ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL); + if(ret != SASL_OK) plist = NULL; + } + + if(!plist) { + /* Do lookup in all plugins */ + + /* TODO: Ideally, each auxprop plugin should be marked if its failure + should be ignored or treated as a fatal error of the whole lookup. */ + for(ptr = auxprop_head; ptr; ptr = ptr->next) { + found=1; + ret = ptr->plug->auxprop_lookup(ptr->plug->glob_context, + sparams, flags, user, ulen); + result = _sasl_account_status (result, ret); + } + } else { + char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL; + + if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return SASL_NOMEM; + thisplugin = freeptr = pluginlist; + + /* Do lookup in all *specified* plugins, in order */ + while(*thisplugin) { + char *p; + int last=0; + + while(*thisplugin && isspace((int)*thisplugin)) thisplugin++; + if(!(*thisplugin)) break; + + for(p = thisplugin;*p != '\0' && !isspace((int)*p); p++); + if(*p == '\0') last = 1; + else *p='\0'; + + for(ptr = auxprop_head; ptr; ptr = ptr->next) { + /* Skip non-matching plugins */ + if(!ptr->plug->name + || strcasecmp(ptr->plug->name, thisplugin)) + continue; + + found=1; + ret = ptr->plug->auxprop_lookup(ptr->plug->glob_context, + sparams, flags, user, ulen); + result = _sasl_account_status (result, ret); + } + + if(last) break; + + thisplugin = p+1; + } + + sasl_FREE(freeptr); + } + + if(!found) { + _sasl_log(sparams->utils->conn, SASL_LOG_DEBUG, + "could not find auxprop plugin, was searching for '%s'", + plist ? plist : "[all]"); + } + + return result; +} + +/* Do the callbacks for auxprop stores */ +int sasl_auxprop_store(sasl_conn_t *conn, + struct propctx *ctx, const char *user) +{ + sasl_getopt_t *getopt; + int ret; + void *context; + const char *plist = NULL; + auxprop_plug_list_t *ptr; + sasl_server_params_t *sparams = NULL; + unsigned userlen = 0; + int num_constraint_violations = 0; + int total_plugins = 0; + + if (ctx) { + if (!conn || !user) + return SASL_BADPARAM; + + sparams = ((sasl_server_conn_t *) conn)->sparams; + userlen = (unsigned) strlen(user); + } + + /* Pickup getopt callback from the connection, if conn is not NULL */ + if(_sasl_getcallback(conn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL); + if(ret != SASL_OK) plist = NULL; + } + + ret = SASL_OK; + if(!plist) { + /* Do store in all plugins */ + for(ptr = auxprop_head; ptr && ret == SASL_OK; ptr = ptr->next) { + total_plugins++; + if (ptr->plug->auxprop_store) { + ret = ptr->plug->auxprop_store(ptr->plug->glob_context, + sparams, ctx, user, userlen); + if (ret == SASL_CONSTRAINT_VIOLAT) { + ret = SASL_OK; + num_constraint_violations++; + } + } + } + } else { + char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL; + + if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return SASL_FAIL; + thisplugin = freeptr = pluginlist; + + /* Do store in all *specified* plugins, in order */ + while(*thisplugin) { + char *p; + int last=0; + + while(*thisplugin && isspace((int)*thisplugin)) thisplugin++; + if(!(*thisplugin)) break; + + for(p = thisplugin;*p != '\0' && !isspace((int)*p); p++); + if(*p == '\0') last = 1; + else *p='\0'; + + for(ptr = auxprop_head; ptr && ret == SASL_OK; ptr = ptr->next) { + /* Skip non-matching plugins */ + if((!ptr->plug->name + || strcasecmp(ptr->plug->name, thisplugin))) + continue; + + total_plugins++; + if (ptr->plug->auxprop_store) { + ret = ptr->plug->auxprop_store(ptr->plug->glob_context, + sparams, ctx, user, userlen); + if (ret == SASL_CONSTRAINT_VIOLAT) { + ret = SASL_OK; + num_constraint_violations++; + } + } + } + + if(last) break; + + thisplugin = p+1; + } + + sasl_FREE(freeptr); + } + + if(total_plugins == 0) { + _sasl_log(NULL, SASL_LOG_ERR, + "could not find auxprop plugin, was searching for %s", + plist ? plist : "[all]"); + return SASL_FAIL; + } else if (total_plugins == num_constraint_violations) { + ret = SASL_CONSTRAINT_VIOLAT; + } + + return ret; +} + +/* It would be nice if we can show other information like Author, Company, Year, plugin version */ +static void +_sasl_print_mechanism (sasl_auxprop_plug_t *m, + sasl_info_callback_stage_t stage, + void *rock __attribute__((unused)) +) +{ + if (stage == SASL_INFO_LIST_START) { + printf ("List of auxprop plugins follows\n"); + return; + } else if (stage == SASL_INFO_LIST_END) { + return; + } + + /* Process the mechanism */ + printf ("Plugin \"%s\" ", m->name); + +#ifdef NOT_YET + switch (m->condition) { + case SASL_OK: + printf ("[loaded]"); + break; + + case SASL_CONTINUE: + printf ("[delayed]"); + break; + + case SASL_NOUSER: + printf ("[no users]"); + break; + + default: + printf ("[unknown]"); + break; + } +#endif + + printf (", \tAPI version: %d\n", /* m->version */ SASL_AUXPROP_PLUG_VERSION); + + /* TODO - Update for auxprop_export, etc. */ + printf ("\tsupports store: %s\n", + (m->auxprop_store != NULL) ? "yes" : "no" + ); + + /* No features defined yet */ +#ifdef NOT_YET + printf ("\n\tfeatures:"); +#endif + + printf ("\n"); +} + +/* Dump information about available auxprop plugins (separate functions are + used for canon and server authentication plugins) */ +int auxprop_plugin_info ( + const char *c_mech_list, /* space separated mechanism list or NULL for ALL */ + auxprop_info_callback_t *info_cb, + void *info_cb_rock +) +{ + auxprop_plug_list_t *m; + sasl_auxprop_plug_t plug_data; + char * cur_mech; + char *mech_list = NULL; + char * p; + + if (info_cb == NULL) { + info_cb = _sasl_print_mechanism; + } + + if (auxprop_head != NULL) { + info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock); + + if (c_mech_list == NULL) { + m = auxprop_head; /* m point to beginning of the list */ + + while (m != NULL) { + /* TODO: Need to be careful when dealing with auxprop_export, etc. */ + memcpy (&plug_data, m->plug, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + + m = m->next; + } + } else { + mech_list = strdup(c_mech_list); + + cur_mech = mech_list; + + while (cur_mech != NULL) { + p = strchr (cur_mech, ' '); + if (p != NULL) { + *p = '\0'; + p++; + } + + m = auxprop_head; /* m point to beginning of the list */ + + while (m != NULL) { + if (strcasecmp (cur_mech, m->plug->name) == 0) { + memcpy (&plug_data, m->plug, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + } + + m = m->next; + } + + cur_mech = p; + } + + free (mech_list); + } + + info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock); + + return (SASL_OK); + } + + return (SASL_NOTINIT); +} diff --git a/contrib/libs/sasl/lib/canonusr.c b/contrib/libs/sasl/lib/canonusr.c new file mode 100644 index 0000000000..66f7e112a6 --- /dev/null +++ b/contrib/libs/sasl/lib/canonusr.c @@ -0,0 +1,465 @@ +/* canonusr.c - user canonicalization support + * Rob Siemborski + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <sasl.h> +#include <string.h> +#include <ctype.h> +#include <prop.h> +#include <stdio.h> + +#include "saslint.h" + +typedef struct canonuser_plug_list +{ + struct canonuser_plug_list *next; + char name[PATH_MAX]; + const sasl_canonuser_plug_t *plug; +} canonuser_plug_list_t; + +static canonuser_plug_list_t *canonuser_head = NULL; + +/* default behavior: + * eliminate leading & trailing whitespace, + * null-terminate, and get into the outparams + * (handled by INTERNAL plugin) */ +/* a zero ulen or alen indicates that it is strlen(value) */ +int _sasl_canon_user(sasl_conn_t *conn, + const char *user, unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams) +{ + canonuser_plug_list_t *ptr; + sasl_server_conn_t *sconn = NULL; + sasl_client_conn_t *cconn = NULL; + sasl_canon_user_t *cuser_cb; + sasl_getopt_t *getopt; + void *context; + int result; + const char *plugin_name = NULL; + char *user_buf; + unsigned *lenp; + + if(!conn) return SASL_BADPARAM; + if(!user || !oparams) return SASL_BADPARAM; + + if(flags & SASL_CU_AUTHID) { + user_buf = conn->authid_buf; + lenp = &(oparams->alen); + } else if (flags & SASL_CU_AUTHZID) { + user_buf = conn->user_buf; + lenp = &(oparams->ulen); + } else { + return SASL_BADPARAM; + } + + if (conn->type == SASL_CONN_SERVER) + sconn = (sasl_server_conn_t *)conn; + else if (conn->type == SASL_CONN_CLIENT) + cconn = (sasl_client_conn_t *)conn; + else return SASL_FAIL; + + if(!ulen) ulen = (unsigned int)strlen(user); + + /* check to see if we have a callback to make*/ + result = _sasl_getcallback(conn, + SASL_CB_CANON_USER, + (sasl_callback_ft *)&cuser_cb, + &context); + if(result == SASL_OK && cuser_cb) { + result = cuser_cb(conn, + context, + user, + ulen, + flags, + (sconn ? + sconn->user_realm : + NULL), + user_buf, + CANON_BUF_SIZE, + lenp); + + + if (result != SASL_OK) return result; + + /* Point the input copy at the stored buffer */ + user = user_buf; + ulen = *lenp; + } + + /* which plugin are we supposed to use? */ + result = _sasl_getcallback(conn, + SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, + &context); + if (result == SASL_OK && getopt) { + getopt(context, NULL, "canon_user_plugin", &plugin_name, NULL); + } + + if (!plugin_name) { + /* Use Default */ + plugin_name = "INTERNAL"; + } + + for (ptr = canonuser_head; ptr; ptr = ptr->next) { + /* A match is if we match the internal name of the plugin, or if + * we match the filename (old-style) */ + if ((ptr->plug->name && !strcmp(plugin_name, ptr->plug->name)) + || !strcmp(plugin_name, ptr->name)) break; + } + + /* We clearly don't have this one! */ + if (!ptr) { + sasl_seterror(conn, 0, "desired canon_user plugin %s not found", + plugin_name); + return SASL_NOMECH; + } + + if (sconn) { + /* we're a server */ + result = ptr->plug->canon_user_server(ptr->plug->glob_context, + sconn->sparams, + user, ulen, + flags, + user_buf, + CANON_BUF_SIZE, lenp); + } else { + /* we're a client */ + result = ptr->plug->canon_user_client(ptr->plug->glob_context, + cconn->cparams, + user, ulen, + flags, + user_buf, + CANON_BUF_SIZE, lenp); + } + + if (result != SASL_OK) return result; + + if ((flags & SASL_CU_AUTHID) && (flags & SASL_CU_AUTHZID)) { + /* We did both, so we need to copy the result into + * the buffer for the authzid from the buffer for the authid */ + memcpy(conn->user_buf, conn->authid_buf, CANON_BUF_SIZE); + oparams->ulen = oparams->alen; + } + + /* Set the appropriate oparams (lengths have already been set by lenp) */ + if (flags & SASL_CU_AUTHID) { + oparams->authid = conn->authid_buf; + } + + if (flags & SASL_CU_AUTHZID) { + oparams->user = conn->user_buf; + } + + RETURN(conn, result); +} + +/* Lookup all properties for authentication and/or authorization identity. */ +static int _sasl_auxprop_lookup_user_props (sasl_conn_t *conn, + unsigned flags, + sasl_out_params_t *oparams) +{ + sasl_server_conn_t *sconn = NULL; + int result = SASL_OK; + + if (!conn) return SASL_BADPARAM; + if (!oparams) return SASL_BADPARAM; + +#ifndef macintosh + if (conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn; + + /* do auxprop lookups (server only) */ + if (sconn) { + int authz_result; + unsigned auxprop_lookup_flags = flags & SASL_CU_ASIS_MASK; + + if (flags & SASL_CU_OVERRIDE) { + auxprop_lookup_flags |= SASL_AUXPROP_OVERRIDE; + } + + if (flags & SASL_CU_AUTHID) { + result = _sasl_auxprop_lookup(sconn->sparams, + auxprop_lookup_flags, + oparams->authid, + oparams->alen); + } else { + result = SASL_CONTINUE; + } + if (flags & SASL_CU_AUTHZID) { + authz_result = _sasl_auxprop_lookup(sconn->sparams, + auxprop_lookup_flags | SASL_AUXPROP_AUTHZID, + oparams->user, + oparams->ulen); + + if (result == SASL_CONTINUE) { + /* Only SASL_CU_AUTHZID was requested. + The authz_result value is authoritative. */ + result = authz_result; + } else if (result == SASL_OK && authz_result != SASL_NOUSER) { + /* Use the authz_result value, unless "result" + already contains an error */ + result = authz_result; + } + } + + if ((flags & SASL_CU_EXTERNALLY_VERIFIED) && (result == SASL_NOUSER || result == SASL_NOMECH)) { + /* The called has explicitly told us that the authentication identity + was already verified or will be verified independently. + So a failure to retrieve any associated properties + is not an error. For example the caller is using Kerberos to verify user, + but the LDAPDB/SASLDB auxprop plugin doesn't contain any auxprops for + the user. + Another case is PLAIN/LOGIN not using auxprop to verify user passwords. */ + result = SASL_OK; + } + } +#endif + + RETURN(conn, result); +} + +/* default behavior: + * Eliminate leading & trailing whitespace, + * null-terminate, and get into the outparams + * (handled by INTERNAL plugin). + * + * Server only: Also does auxprop lookups once username + * is canonicalized. */ +int _sasl_canon_user_lookup (sasl_conn_t *conn, + const char *user, + unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams) +{ + int result; + + result = _sasl_canon_user (conn, + user, + ulen, + flags, + oparams); + if (result == SASL_OK) { + result = _sasl_auxprop_lookup_user_props (conn, + flags, + oparams); + } + + RETURN(conn, result); +} + +void _sasl_canonuser_free() +{ + canonuser_plug_list_t *ptr, *ptr_next; + + for(ptr = canonuser_head; ptr; ptr = ptr_next) { + ptr_next = ptr->next; + if(ptr->plug->canon_user_free) + ptr->plug->canon_user_free(ptr->plug->glob_context, + sasl_global_utils); + sasl_FREE(ptr); + } + + canonuser_head = NULL; +} + +int sasl_canonuser_add_plugin(const char *plugname, + sasl_canonuser_init_t *canonuserfunc) +{ + int result, out_version; + canonuser_plug_list_t *new_item; + sasl_canonuser_plug_t *plug; + + if(!plugname || strlen(plugname) > (PATH_MAX - 1)) { + sasl_seterror(NULL, 0, + "bad plugname passed to sasl_canonuser_add_plugin\n"); + return SASL_BADPARAM; + } + + result = canonuserfunc(sasl_global_utils, SASL_CANONUSER_PLUG_VERSION, + &out_version, &plug, plugname); + + if(result != SASL_OK) { + _sasl_log(NULL, SASL_LOG_ERR, "%s_canonuser_plug_init() failed in sasl_canonuser_add_plugin(): %z\n", + plugname, result); + return result; + } + + if(!plug->canon_user_server && !plug->canon_user_client) { + /* We need at least one of these implemented */ + _sasl_log(NULL, SASL_LOG_ERR, + "canonuser plugin '%s' without either client or server side", plugname); + return SASL_BADPROT; + } + + new_item = sasl_ALLOC(sizeof(canonuser_plug_list_t)); + if(!new_item) return SASL_NOMEM; + + strncpy(new_item->name, plugname, PATH_MAX - 1); + new_item->name[strlen(plugname)] = '\0'; + + new_item->plug = plug; + new_item->next = canonuser_head; + canonuser_head = new_item; + + return SASL_OK; +} + +#ifdef MIN +#undef MIN +#endif +#define MIN(a,b) (((a) < (b))? (a):(b)) + +static int _canonuser_internal(const sasl_utils_t *utils, + const char *user, unsigned ulen, + unsigned flags __attribute__((unused)), + char *out_user, + unsigned out_umax, unsigned *out_ulen) +{ + unsigned i; + char *in_buf, *userin; + const char *begin_u; + unsigned u_apprealm = 0; + sasl_server_conn_t *sconn = NULL; + + if(!utils || !user) return SASL_BADPARAM; + + in_buf = sasl_ALLOC((ulen + 2) * sizeof(char)); + if(!in_buf) return SASL_NOMEM; + + userin = in_buf; + + memcpy(userin, user, ulen); + userin[ulen] = '\0'; + + /* Strip User ID */ + for(i=0;isspace((int)userin[i]) && i<ulen;i++); + begin_u = &(userin[i]); + if(i>0) ulen -= i; + + for(;ulen > 0 && isspace((int)begin_u[ulen-1]); ulen--); + if(begin_u == &(userin[ulen])) { + sasl_FREE(in_buf); + utils->seterror(utils->conn, 0, "All-whitespace username."); + return SASL_FAIL; + } + + if(utils->conn && utils->conn->type == SASL_CONN_SERVER) + sconn = (sasl_server_conn_t *)utils->conn; + + /* Need to append realm if necessary (see sasl.h) */ + if(sconn && sconn->user_realm && !strchr(user, '@')) { + u_apprealm = (unsigned) strlen(sconn->user_realm) + 1; + } + + /* Now Copy */ + memcpy(out_user, begin_u, MIN(ulen, out_umax)); + if(sconn && u_apprealm) { + if(ulen >= out_umax) return SASL_BUFOVER; + out_user[ulen] = '@'; + memcpy(&(out_user[ulen+1]), sconn->user_realm, + MIN(u_apprealm-1, out_umax-ulen-1)); + } + out_user[MIN(ulen + u_apprealm,out_umax)] = '\0'; + + if(ulen + u_apprealm > out_umax) return SASL_BUFOVER; + + if(out_ulen) *out_ulen = MIN(ulen + u_apprealm,out_umax); + + sasl_FREE(in_buf); + return SASL_OK; +} + +static int _cu_internal_server(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *user, unsigned ulen, + unsigned flags, + char *out_user, + unsigned out_umax, unsigned *out_ulen) +{ + return _canonuser_internal(sparams->utils, + user, ulen, + flags, out_user, out_umax, out_ulen); +} + +static int _cu_internal_client(void *glob_context __attribute__((unused)), + sasl_client_params_t *cparams, + const char *user, unsigned ulen, + unsigned flags, + char *out_user, + unsigned out_umax, unsigned *out_ulen) +{ + return _canonuser_internal(cparams->utils, + user, ulen, + flags, out_user, out_umax, out_ulen); +} + +static sasl_canonuser_plug_t canonuser_internal_plugin = { + 0, /* features */ + 0, /* spare */ + NULL, /* glob_context */ + "INTERNAL", /* name */ + NULL, /* canon_user_free */ + _cu_internal_server, + _cu_internal_client, + NULL, + NULL, + NULL +}; + +int internal_canonuser_init(const sasl_utils_t *utils __attribute__((unused)), + int max_version, + int *out_version, + sasl_canonuser_plug_t **plug, + const char *plugname __attribute__((unused))) +{ + if(!out_version || !plug) return SASL_BADPARAM; + + if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS; + + *out_version = SASL_CANONUSER_PLUG_VERSION; + + *plug = &canonuser_internal_plugin; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/lib/checkpw.c b/contrib/libs/sasl/lib/checkpw.c new file mode 100644 index 0000000000..a13f526b7c --- /dev/null +++ b/contrib/libs/sasl/lib/checkpw.c @@ -0,0 +1,1110 @@ +/* SASL server API implementation + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> + +/* checkpw stuff */ + +#include <stdio.h> +#include "sasl.h" +#include "saslutil.h" +#include "saslplug.h" +#include "saslint.h" + +#include <assert.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <fcntl.h> +#ifdef USE_DOORS +#include <sys/mman.h> +#error #include <door.h> +#endif + +#include <stdlib.h> + +#ifndef WIN32 +#include <strings.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/un.h> +#else +#include <string.h> +#endif + +#include <limits.h> +#include <sys/types.h> +#include <ctype.h> + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif /* HAVE_PWD_H */ +#ifdef HAVE_SHADOW_H +#include <shadow.h> +#endif /* HAVE_SHADOW_H */ + +#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON) +# include <errno.h> +# include <sys/types.h> +# include <sys/socket.h> +# include <sys/un.h> +# ifdef HAVE_UNISTD_H +# include <unistd.h> +# endif +#endif + + +/* we store the following secret to check plaintext passwords: + * + * <salt> \0 <secret> + * + * where <secret> = MD5(<salt>, "sasldb", <pass>) + */ +static int _sasl_make_plain_secret(const char *salt, + const char *passwd, size_t passlen, + sasl_secret_t **secret) +{ + MD5_CTX ctx; + unsigned sec_len = 16 + 1 + 16; /* salt + "\0" + hash */ + + *secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) + + sec_len * sizeof(char)); + if (*secret == NULL) { + return SASL_NOMEM; + } + + _sasl_MD5Init(&ctx); + _sasl_MD5Update(&ctx, (const unsigned char *) salt, 16); + _sasl_MD5Update(&ctx, (const unsigned char *) "sasldb", 6); + _sasl_MD5Update(&ctx, (const unsigned char *) passwd, (unsigned int) passlen); + memcpy((*secret)->data, salt, 16); + (*secret)->data[16] = '\0'; + _sasl_MD5Final((*secret)->data + 17, &ctx); + (*secret)->len = sec_len; + + return SASL_OK; +} + +/* verify user password using auxprop plugins + */ +static int auxprop_verify_password(sasl_conn_t *conn, + const char *userstr, + const char *passwd, + const char *service __attribute__((unused)), + const char *user_realm __attribute__((unused))) +{ + int ret = SASL_FAIL; + int result = SASL_OK; + sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn; + const char *password_request[] = { SASL_AUX_PASSWORD, + "*cmusaslsecretPLAIN", + NULL }; + struct propval auxprop_values[3]; + + if (!conn || !userstr) + return SASL_BADPARAM; + + /* We need to clear any previous results and re-canonify to + * ensure correctness */ + + prop_clear (sconn->sparams->propctx, 0); + + /* ensure its requested */ + result = prop_request(sconn->sparams->propctx, password_request); + + if(result != SASL_OK) return result; + + result = _sasl_canon_user_lookup (conn, + userstr, + 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + &(conn->oparams)); + if(result != SASL_OK) return result; + + result = prop_getnames(sconn->sparams->propctx, password_request, + auxprop_values); + if (result < 0) { + return result; + } + + /* Verify that the returned <name>s are correct. + But we defer checking for NULL values till after we verify + that a passwd is specified. */ + if (!auxprop_values[0].name && !auxprop_values[1].name) { + return SASL_NOUSER; + } + + /* It is possible for us to get useful information out of just + * the lookup, so we won't check that we have a password until now */ + if(!passwd) { + ret = SASL_BADPARAM; + goto done; + } + + if ((!auxprop_values[0].values || !auxprop_values[0].values[0]) + && (!auxprop_values[1].values || !auxprop_values[1].values[0])) { + return SASL_NOUSER; + } + + /* At the point this has been called, the username has been canonified + * and we've done the auxprop lookup. This should be easy. */ + if(auxprop_values[0].name + && auxprop_values[0].values + && auxprop_values[0].values[0] + && !strcmp(auxprop_values[0].values[0], passwd)) { + /* We have a plaintext version and it matched! */ + return SASL_OK; + } else if(auxprop_values[1].name + && auxprop_values[1].values + && auxprop_values[1].values[0]) { + const char *db_secret = auxprop_values[1].values[0]; + sasl_secret_t *construct; + + ret = _sasl_make_plain_secret(db_secret, passwd, + strlen(passwd), + &construct); + if (ret != SASL_OK) { + goto done; + } + + if (!memcmp(db_secret, construct->data, construct->len)) { + /* password verified! */ + ret = SASL_OK; + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + sasl_FREE(construct); + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + /* erase the plaintext password */ + sconn->sparams->utils->prop_erase(sconn->sparams->propctx, + password_request[0]); + + done: + /* We're not going to erase the property here because other people + * may want it */ + return ret; +} + +#if 0 +/* Verify user password using auxprop plugins. Allow verification against a hashed password, + * or non-retrievable password. Don't use cmusaslsecretPLAIN attribute. + * + * This function is similar to auxprop_verify_password(). + */ +static int auxprop_verify_password_hashed(sasl_conn_t *conn, + const char *userstr, + const char *passwd, + const char *service __attribute__((unused)), + const char *user_realm __attribute__((unused))) +{ + int ret = SASL_FAIL; + int result = SASL_OK; + sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn; + const char *password_request[] = { SASL_AUX_PASSWORD, + NULL }; + struct propval auxprop_values[2]; + unsigned extra_cu_flags = 0; + + if (!conn || !userstr) + return SASL_BADPARAM; + + /* We need to clear any previous results and re-canonify to + * ensure correctness */ + + prop_clear(sconn->sparams->propctx, 0); + + /* ensure its requested */ + result = prop_request(sconn->sparams->propctx, password_request); + + if (result != SASL_OK) return result; + + /* We need to pass "password" down to the auxprop_lookup */ + /* NB: We don't support binary passwords */ + if (passwd != NULL) { + prop_set (sconn->sparams->propctx, + SASL_AUX_PASSWORD, + passwd, + -1); + extra_cu_flags = SASL_CU_VERIFY_AGAINST_HASH; + } + + result = _sasl_canon_user_lookup (conn, + userstr, + 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID | extra_cu_flags, + &(conn->oparams)); + + if (result != SASL_OK) return result; + + result = prop_getnames(sconn->sparams->propctx, password_request, + auxprop_values); + if (result < 0) { + return result; + } + + /* Verify that the returned <name>s are correct. + But we defer checking for NULL values till after we verify + that a passwd is specified. */ + if (!auxprop_values[0].name && !auxprop_values[1].name) { + return SASL_NOUSER; + } + + /* It is possible for us to get useful information out of just + * the lookup, so we won't check that we have a password until now */ + if (!passwd) { + ret = SASL_BADPARAM; + goto done; + } + + if ((!auxprop_values[0].values || !auxprop_values[0].values[0])) { + return SASL_NOUSER; + } + + /* At the point this has been called, the username has been canonified + * and we've done the auxprop lookup. This should be easy. */ + + /* NB: Note that if auxprop_lookup failed to verify the password, + then the userPassword property value would be NULL */ + if (auxprop_values[0].name + && auxprop_values[0].values + && auxprop_values[0].values[0] + && !strcmp(auxprop_values[0].values[0], passwd)) { + /* We have a plaintext version and it matched! */ + return SASL_OK; + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + done: + /* We're not going to erase the property here because other people + * may want it */ + return ret; +} +#endif + +#ifdef DO_SASL_CHECKAPOP +int _sasl_auxprop_verify_apop(sasl_conn_t *conn, + const char *userstr, + const char *challenge, + const char *response, + const char *user_realm __attribute__((unused))) +{ + int ret = SASL_BADAUTH; + char *userid = NULL; + char *realm = NULL; + unsigned char digest[16]; + char digeststr[33]; + const char *password_request[] = { SASL_AUX_PASSWORD, NULL }; + struct propval auxprop_values[2]; + sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn; + MD5_CTX ctx; + int i; + + if (!conn || !userstr || !challenge || !response) + PARAMERROR(conn) + + /* We've done the auxprop lookup already (in our caller) */ + /* sadly, APOP has no provision for storing secrets */ + ret = prop_getnames(sconn->sparams->propctx, password_request, + auxprop_values); + if(ret < 0) { + sasl_seterror(conn, 0, "could not perform password lookup"); + goto done; + } + + if(!auxprop_values[0].name || + !auxprop_values[0].values || + !auxprop_values[0].values[0]) { + sasl_seterror(conn, 0, "could not find password"); + ret = SASL_NOUSER; + goto done; + } + + _sasl_MD5Init(&ctx); + _sasl_MD5Update(&ctx, (const unsigned char *) challenge, strlen(challenge)); + _sasl_MD5Update(&ctx, (const unsigned char *) auxprop_values[0].values[0], + strlen(auxprop_values[0].values[0])); + _sasl_MD5Final(digest, &ctx); + + /* erase the plaintext password */ + sconn->sparams->utils->prop_erase(sconn->sparams->propctx, + password_request[0]); + + /* convert digest from binary to ASCII hex */ + for (i = 0; i < 16; i++) + sprintf(digeststr + (i*2), "%02x", digest[i]); + + if (!strncasecmp(digeststr, response, 32)) { + /* password verified! */ + ret = SASL_OK; + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + done: + if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG, + "login incorrect"); + if (userid) sasl_FREE(userid); + if (realm) sasl_FREE(realm); + + return ret; +} +#endif /* DO_SASL_CHECKAPOP */ + +#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON) +/* + * Wait for file descriptor to be writable. Return with error if timeout. + */ +static int write_wait(int fd, unsigned delta) +{ + fd_set wfds; + fd_set efds; + struct timeval tv; + + /* + * Wait for file descriptor fd to be writable. Retry on + * interruptions. Return with error upon timeout. + */ + while (1) { + FD_ZERO(&wfds); + FD_ZERO(&efds); + FD_SET(fd, &wfds); + FD_SET(fd, &efds); + tv.tv_sec = (long) delta; + tv.tv_usec = 0; + switch(select(fd + 1, 0, &wfds, &efds, &tv)) { + case 0: + /* Timeout. */ + errno = ETIMEDOUT; + return -1; + case +1: + if (FD_ISSET(fd, &wfds)) { + /* Success, file descriptor is writable. */ + return 0; + } + return -1; + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + default: + /* Error catch-all. */ + return -1; + } + } + /* Not reached. */ + return -1; +} + +/* + * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt' + * until all the data is written out or an error/timeout occurs. + */ +static int retry_writev(int fd, struct iovec *iov, int iovcnt, unsigned delta) +{ + int n; + int i; + int written = 0; + static int iov_max = +#ifdef MAXIOV + MAXIOV +#else +#ifdef IOV_MAX + IOV_MAX +#else + 8192 +#endif +#endif + ; + + for (;;) { + while (iovcnt && iov[0].iov_len == 0) { + iov++; + iovcnt--; + } + + if (!iovcnt) return written; + + if (delta > 0) { + if (write_wait(fd, delta)) + return -1; + } + n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt); + if (n == -1) { + if (errno == EINVAL && iov_max > 10) { + iov_max /= 2; + continue; + } + if (errno == EINTR) continue; + return -1; + } + + written += n; + + for (i = 0; i < iovcnt; i++) { + if ((int) iov[i].iov_len > n) { + iov[i].iov_base = (char *)iov[i].iov_base + n; + iov[i].iov_len -= n; + break; + } + n -= iov[i].iov_len; + iov[i].iov_len = 0; + } + + if (i == iovcnt) return written; + } +} + +#endif + +#ifdef HAVE_PWCHECK +/* pwcheck daemon-authenticated login */ +static int pwcheck_verify_password(sasl_conn_t *conn, + const char *userid, + const char *passwd, + const char *service __attribute__((unused)), + const char *user_realm + __attribute__((unused))) +{ + int s; + struct sockaddr_un srvaddr; + int r; + struct iovec iov[10]; + static char response[1024]; + unsigned start, n; + char pwpath[1024]; + + if (strlen(PWCHECKDIR)+8+1 > sizeof(pwpath)) return SASL_FAIL; + + strcpy(pwpath, PWCHECKDIR); + strcat(pwpath, "/pwcheck"); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) return errno; + + memset((char *)&srvaddr, 0, sizeof(srvaddr)); + srvaddr.sun_family = AF_UNIX; + strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path)); + r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr)); + if (r == -1) { + sasl_seterror(conn,0,"cannot connect to pwcheck server"); + return SASL_FAIL; + } + + iov[0].iov_base = (char *)userid; + iov[0].iov_len = strlen(userid)+1; + iov[1].iov_base = (char *)passwd; + iov[1].iov_len = strlen(passwd)+1; + + retry_writev(s, iov, 2, 0); + + start = 0; + while (start < sizeof(response) - 1) { + n = read(s, response+start, sizeof(response) - 1 - start); + if (n < 1) break; + start += n; + } + + close(s); + + if (start > 1 && !strncmp(response, "OK", 2)) { + return SASL_OK; + } + + response[start] = '\0'; + sasl_seterror(conn,0,response); + return SASL_BADAUTH; +} + +#endif + +#if defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON) +static int read_wait(int fd, unsigned delta) +{ + fd_set rfds; + fd_set efds; + struct timeval tv; + /* + * Wait for file descriptor fd to be readable. Retry on + * interruptions. Return with error upon timeout. + */ + while (1) { + FD_ZERO(&rfds); + FD_ZERO(&efds); + FD_SET(fd, &rfds); + FD_SET(fd, &efds); + tv.tv_sec = (long) delta; + tv.tv_usec = 0; + switch(select(fd + 1, &rfds, 0, &efds, &tv)) { + case 0: + /* Timeout. */ + errno = ETIMEDOUT; + return -1; + case +1: + case +2: + if (FD_ISSET(fd, &rfds)) { + /* Success, file descriptor is readable. */ + return 0; + } + return -1; + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + default: + /* Error catch-all. */ + return -1; + } + } + /* Not reached. */ + return -1; +} + +/* + * Keep calling the read() system call until all the data is read in, + * timeout, EOF, or an error occurs. This function returns the number + * of useful bytes, or -1 if timeout/error. + */ +static int retry_read(int fd, void *buf0, unsigned nbyte, unsigned delta) +{ + int nr; + unsigned nleft = nbyte; + char *buf = (char*) buf0; + + while (nleft >= 1) { + if (delta > 0) { + if (read_wait(fd, delta)) + return -1; + } + nr = read(fd, buf, nleft); + if (nr < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } else if (nr == 0) { + break; + } + buf += nr; + nleft -= nr; + } + return nbyte - nleft; +} +#endif + +#ifdef HAVE_SASLAUTHD +/* saslauthd-authenticated login */ +static int saslauthd_verify_password(sasl_conn_t *conn, + const char *userid, + const char *passwd, + const char *service, + const char *user_realm) +{ + char response[1024]; + char query[8192]; + char *query_end = query; + int s; + struct sockaddr_un srvaddr; + sasl_getopt_t *getopt; + void *context; + char pwpath[sizeof(srvaddr.sun_path)]; + const char *p = NULL; + char *freeme = NULL; +#ifdef USE_DOORS + door_arg_t arg; +#endif + + /* check to see if the user configured a rundir */ + if (_sasl_getcallback(conn, SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + getopt(context, NULL, "saslauthd_path", &p, NULL); + } + if (p) { + if (strlen(p) >= sizeof(pwpath)) + return SASL_FAIL; + + strncpy(pwpath, p, sizeof(pwpath) - 1); + pwpath[strlen(p)] = '\0'; + } else { + if (strlen(PATH_SASLAUTHD_RUNDIR) + 4 + 1 > sizeof(pwpath)) + return SASL_FAIL; + + strcpy(pwpath, PATH_SASLAUTHD_RUNDIR "/mux"); + } + + /* Split out username/realm if necessary */ + if(strrchr(userid,'@') != NULL) { + char *rtmp; + + if(_sasl_strdup(userid, &freeme, NULL) != SASL_OK) + goto fail; + + userid = freeme; + rtmp = strrchr(userid,'@'); + *rtmp = '\0'; + user_realm = rtmp + 1; + } + + /* + * build request of the form: + * + * count authid count password count service count realm + */ + { + unsigned short max_len, req_len, u_len, p_len, s_len, r_len; + + max_len = (unsigned short) sizeof(query); + + /* prevent buffer overflow */ + if ((strlen(userid) > USHRT_MAX) || + (strlen(passwd) > USHRT_MAX) || + (strlen(service) > USHRT_MAX) || + (user_realm && (strlen(user_realm) > USHRT_MAX))) { + goto toobig; + } + + u_len = (strlen(userid)); + p_len = (strlen(passwd)); + s_len = (strlen(service)); + r_len = ((user_realm ? strlen(user_realm) : 0)); + + /* prevent buffer overflow */ + req_len = 30; + if (max_len - req_len < u_len) goto toobig; + req_len += u_len; + if (max_len - req_len < p_len) goto toobig; + req_len += p_len; + if (max_len - req_len < s_len) goto toobig; + req_len += s_len; + if (max_len - req_len < r_len) goto toobig; + + u_len = htons(u_len); + p_len = htons(p_len); + s_len = htons(s_len); + r_len = htons(r_len); + + memcpy(query_end, &u_len, sizeof(unsigned short)); + query_end += sizeof(unsigned short); + while (*userid) *query_end++ = *userid++; + + memcpy(query_end, &p_len, sizeof(unsigned short)); + query_end += sizeof(unsigned short); + while (*passwd) *query_end++ = *passwd++; + + memcpy(query_end, &s_len, sizeof(unsigned short)); + query_end += sizeof(unsigned short); + while (*service) *query_end++ = *service++; + + memcpy(query_end, &r_len, sizeof(unsigned short)); + query_end += sizeof(unsigned short); + if (user_realm) while (*user_realm) *query_end++ = *user_realm++; + } + +#ifdef USE_DOORS + s = open(pwpath, O_RDONLY); + if (s < 0) { + sasl_seterror(conn, 0, "cannot open door to saslauthd server: %m", errno); + goto fail; + } + + arg.data_ptr = query; + arg.data_size = query_end - query; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = response; + arg.rsize = sizeof(response); + + if (door_call(s, &arg) < 0) { + /* Parameters are undefined */ + close(s); + sasl_seterror(conn, 0, "door call to saslauthd server failed: %m", errno); + goto fail; + } + + if (arg.data_ptr != response || arg.data_size >= sizeof(response)) { + /* oh damn, we got back a really long response */ + munmap(arg.rbuf, arg.rsize); + close(s); + sasl_seterror(conn, 0, "saslauthd sent an overly long response"); + goto fail; + } + response[arg.data_size] = '\0'; + + close(s); +#else + /* unix sockets */ + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) { + sasl_seterror(conn, 0, "cannot create socket for saslauthd: %m", errno); + goto fail; + } + + memset((char *)&srvaddr, 0, sizeof(srvaddr)); + srvaddr.sun_family = AF_UNIX; + strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path) - 1); + srvaddr.sun_path[strlen(pwpath)] = '\0'; + + { + int r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr)); + if (r == -1) { + close(s); + sasl_seterror(conn, 0, "cannot connect to saslauthd server: %m", errno); + goto fail; + } + } + + { + struct iovec iov[8]; + + iov[0].iov_len = query_end - query; + iov[0].iov_base = query; + + if (retry_writev(s, iov, 1, 0) == -1) { + close(s); + sasl_seterror(conn, 0, "write failed"); + goto fail; + } + } + + { + unsigned short count = 0; + + /* + * read response of the form: + * + * count result + */ + if (retry_read(s, &count, sizeof(count), 0) < (int) sizeof(count)) { + sasl_seterror(conn, 0, "size read failed"); + goto fail; + } + + count = ntohs(count); + if (count < 2) { /* MUST have at least "OK" or "NO" */ + close(s); + sasl_seterror(conn, 0, "bad response from saslauthd"); + goto fail; + } + + count = (int)sizeof(response) <= count ? sizeof(response) - 1 : count; + if (retry_read(s, response, count, 0) < count) { + close(s); + sasl_seterror(conn, 0, "read failed"); + goto fail; + } + response[count] = '\0'; + } + + close(s); +#endif /* USE_DOORS */ + + if(freeme) free(freeme); + + if (!strncmp(response, "OK", 2)) { + return SASL_OK; + } + + sasl_seterror(conn, SASL_NOLOG, "authentication failed"); + return SASL_BADAUTH; + + toobig: + /* request just too damn big */ + sasl_seterror(conn, 0, "saslauthd request too large"); + + fail: + if (freeme) free(freeme); + return SASL_FAIL; +} + +#endif + +#ifdef HAVE_AUTHDAEMON +/* + * Preliminary support for Courier's authdaemond. + */ +#define AUTHDAEMON_IO_TIMEOUT 30 + +static int authdaemon_blocking(int fd, int block) +{ + int f, r; + + /* Get the fd's blocking bit. */ + f = fcntl(fd, F_GETFL, 0); + if (f == -1) + return -1; + + /* Adjust the bitmap accordingly. */ +#ifndef O_NONBLOCK +#define NB_BITMASK FNDELAY +#else +#define NB_BITMASK O_NONBLOCK +#endif + if (block) + f &= ~NB_BITMASK; + else + f |= NB_BITMASK; +#undef NB_BITMASK + + /* Adjust the fd's blocking bit. */ + r = fcntl(fd, F_SETFL, f); + if (r) + return -1; + + /* Success. */ + return 0; +} + +static int authdaemon_connect(sasl_conn_t *conn, const char *path) +{ + int r, s = -1; + struct sockaddr_un srvaddr; + + if (strlen(path) >= sizeof(srvaddr.sun_path)) { + sasl_seterror(conn, 0, "unix socket path too large", errno); + goto fail; + } + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) { + sasl_seterror(conn, 0, "cannot create socket for connection to Courier authdaemond: %m", errno); + goto fail; + } + + memset((char *)&srvaddr, 0, sizeof(srvaddr)); + srvaddr.sun_family = AF_UNIX; + strncpy(srvaddr.sun_path, path, sizeof(srvaddr.sun_path) - 1); + + /* Use nonblocking unix socket connect(2). */ + if (authdaemon_blocking(s, 0)) { + sasl_seterror(conn, 0, "cannot set nonblocking bit: %m", errno); + goto fail; + } + + r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr)); + if (r == -1) { + sasl_seterror(conn, 0, "cannot connect to Courier authdaemond: %m", errno); + goto fail; + } + + if (authdaemon_blocking(s, 1)) { + sasl_seterror(conn, 0, "cannot clear nonblocking bit: %m", errno); + goto fail; + } + + return s; +fail: + if (s >= 0) + close(s); + return -1; +} + +static char *authdaemon_build_query(const char *service, + const char *authtype, + const char *user, + const char *passwd) +{ + int sz; + int l = strlen(service) + + 1 + + strlen(authtype) + + 1 + + strlen(user) + + 1 + + strlen(passwd) + + 1; + char *buf, n[5]; + if (snprintf(n, sizeof(n), "%d", l) >= (int)sizeof(n)) + return NULL; + sz = strlen(n) + l + 20; + if (!(buf = sasl_ALLOC(sz))) + return NULL; + snprintf(buf, + sz, + "AUTH %s\n%s\n%s\n%s\n%s\n\n", + n, + service, + authtype, + user, + passwd); + return buf; +} + +static int authdaemon_read(int fd, void *buf0, unsigned sz) +{ + int nr; + char *buf = (char*) buf0; + if (sz <= 1) + return -1; + if ((nr = retry_read(fd, buf0, sz - 1, AUTHDAEMON_IO_TIMEOUT)) < 0) + return -1; + /* We need a null-terminated buffer. */ + buf[nr] = 0; + /* Check for overflow condition. */ + return nr + 1 < (int)sz ? 0 : -1; +} + +static int authdaemon_write(int fd, void *buf0, unsigned sz) +{ + int nw; + struct iovec io; + io.iov_len = sz; + io.iov_base = buf0; + nw = retry_writev(fd, &io, 1, AUTHDAEMON_IO_TIMEOUT); + return nw == (int)sz ? 0 : -1; +} + +static int authdaemon_talk(sasl_conn_t *conn, int sock, char *authreq) +{ + char *str; + char buf[8192]; + + if (authdaemon_write(sock, authreq, strlen(authreq))) + goto _err_out; + if (authdaemon_read(sock, buf, sizeof(buf))) + goto _err_out; + for (str = buf; *str; ) { + char *sub; + + for (sub = str; *str; ++str) { + if (*str == '\n') { + *str++ = 0; + break; + } + } + if (strcmp(sub, ".") == 0) { + /* success */ + return SASL_OK; + } + if (strcmp(sub, "FAIL") == 0) { + /* passwords do not match */ + sasl_seterror(conn, SASL_NOLOG, "authentication failed"); + return SASL_BADAUTH; + } + } +_err_out: + /* catchall: authentication error */ + sasl_seterror(conn, 0, "could not verify password"); + return SASL_FAIL; +} + +static int authdaemon_verify_password(sasl_conn_t *conn, + const char *userid, + const char *passwd, + const char *service, + const char *user_realm __attribute__((unused))) +{ + const char *p = NULL; + sasl_getopt_t *getopt; + void *context; + int result = SASL_FAIL; + char *query = NULL; + int sock = -1; + + /* check to see if the user configured a rundir */ + if (_sasl_getcallback(conn, SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + getopt(context, NULL, "authdaemond_path", &p, NULL); + } + if (!p) { + /* + * XXX should we peek at Courier's build-time config ? + */ + p = PATH_AUTHDAEMON_SOCKET; + } + + if ((sock = authdaemon_connect(conn, p)) < 0) + goto out; + if (!(query = authdaemon_build_query(service, "login", userid, passwd))) + goto out; + result = authdaemon_talk(conn, sock, query); +out: + if (sock >= 0) + close(sock), sock = -1; + if (query) + sasl_FREE(query), query = 0; + return result; +} +#endif + +#ifdef HAVE_ALWAYSTRUE +static int always_true(sasl_conn_t *conn, + const char *userstr, + const char *passwd __attribute__((unused)), + const char *service __attribute__((unused)), + const char *user_realm __attribute__((unused))) +{ + _sasl_log(conn, SASL_LOG_WARN, "AlwaysTrue Password Verifier Verified: %s", + userstr); + return SASL_OK; +} +#endif + +struct sasl_verify_password_s _sasl_verify_password[] = { + { "auxprop", &auxprop_verify_password }, +#if 0 /* totally undocumented. wtf is this? */ + { "auxprop-hashed", &auxprop_verify_password_hashed }, +#endif +#ifdef HAVE_PWCHECK + { "pwcheck", &pwcheck_verify_password }, +#endif +#ifdef HAVE_SASLAUTHD + { "saslauthd", &saslauthd_verify_password }, +#endif +#ifdef HAVE_AUTHDAEMON + { "authdaemond", &authdaemon_verify_password }, +#endif +#ifdef HAVE_ALWAYSTRUE + { "alwaystrue", &always_true }, +#endif + { NULL, NULL } +}; diff --git a/contrib/libs/sasl/lib/client.c b/contrib/libs/sasl/lib/client.c new file mode 100644 index 0000000000..3784bb0e42 --- /dev/null +++ b/contrib/libs/sasl/lib/client.c @@ -0,0 +1,1317 @@ +/* SASL client API implementation + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <ctype.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/* SASL Headers */ +#include "sasl.h" +#include "saslplug.h" +#include "saslutil.h" +#include "saslint.h" + +static cmech_list_t *cmechlist; /* global var which holds the list */ +static sasl_global_callbacks_t global_callbacks_client; +static int _sasl_client_active = 0; + +static int init_mechlist() +{ + cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks_client); + if (cmechlist->utils==NULL) + return SASL_NOMEM; + + cmechlist->mech_list=NULL; + cmechlist->mech_length=0; + + return SASL_OK; +} + +int sasl_client_done(void) +{ + int result = SASL_CONTINUE; + + if (_sasl_server_cleanup_hook == NULL && _sasl_client_cleanup_hook == NULL) { + return SASL_NOTINIT; + } + + if (_sasl_client_cleanup_hook) { + result = _sasl_client_cleanup_hook(); + + if (result == SASL_OK) { + _sasl_client_idle_hook = NULL; + _sasl_client_cleanup_hook = NULL; + } else { + return result; + } + } + + if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { + return result; + } + + sasl_common_done(); + + return SASL_OK; +} + +static int client_done(void) { + cmechanism_t *cm; + cmechanism_t *cprevm; + + if (!_sasl_client_active) { + return SASL_NOTINIT; + } else { + _sasl_client_active--; + } + + if(_sasl_client_active) { + /* Don't de-init yet! Our refcount is nonzero. */ + return SASL_CONTINUE; + } + + cm = cmechlist->mech_list; /* m point to beginning of the list */ + while (cm != NULL) { + cprevm = cm; + cm = cm->next; + + if (cprevm->m.plug->mech_free) { + cprevm->m.plug->mech_free(cprevm->m.plug->glob_context, + cmechlist->utils); + } + + sasl_FREE(cprevm->m.plugname); + sasl_FREE(cprevm); + } + _sasl_free_utils(&cmechlist->utils); + sasl_FREE(cmechlist); + + cmechlist = NULL; + + return SASL_OK; +} + +/* This is nearly identical to the version in server.c. + Keep in sync. */ +static int mech_compare(const sasl_client_plug_t *a, + const sasl_client_plug_t *b) +{ + unsigned sec_diff; + unsigned features_diff; + + /* XXX the following is fairly arbitrary, but its independent + of the order in which the plugins are loaded + */ +#ifdef PREFER_MECH + if (!strcasecmp(a->mech_name, PREFER_MECH)) return 1; + if (!strcasecmp(b->mech_name, PREFER_MECH)) return -1; +#endif + + sec_diff = a->security_flags ^ b->security_flags; + if (sec_diff & a->security_flags & SASL_SEC_NOANONYMOUS) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOANONYMOUS) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NOPLAINTEXT) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOPLAINTEXT) return -1; + if (sec_diff & a->security_flags & SASL_SEC_MUTUAL_AUTH) return 1; + if (sec_diff & b->security_flags & SASL_SEC_MUTUAL_AUTH) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NOACTIVE) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOACTIVE) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NODICTIONARY) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NODICTIONARY) return -1; + if (sec_diff & a->security_flags & SASL_SEC_FORWARD_SECRECY) return 1; + if (sec_diff & b->security_flags & SASL_SEC_FORWARD_SECRECY) return -1; + + features_diff = a->features ^ b->features; + if (features_diff & a->features & SASL_FEAT_CHANNEL_BINDING) return 1; + if (features_diff & b->features & SASL_FEAT_CHANNEL_BINDING) return -1; + + if (a->max_ssf > b->max_ssf) return 1; + if (a->max_ssf < b->max_ssf) return -1; + + if (SASL_GET_HASH_STRENGTH(a->security_flags) > SASL_GET_HASH_STRENGTH(b->security_flags)) return 1; + if (SASL_GET_HASH_STRENGTH(a->security_flags) < SASL_GET_HASH_STRENGTH(b->security_flags)) return -1; + + return 0; +} + +int sasl_client_add_plugin(const char *plugname, + sasl_client_plug_init_t *entry_point) +{ + int plugcount; + sasl_client_plug_t *pluglist; + cmechanism_t *mech, *mp; + int result; + int version; + int lupe; + + if (!plugname || !entry_point) return SASL_BADPARAM; + + result = entry_point(cmechlist->utils, + SASL_CLIENT_PLUG_VERSION, + &version, + &pluglist, + &plugcount); + + if (result != SASL_OK) + { + _sasl_log(NULL, SASL_LOG_WARN, + "sasl_client_add_plugin(): entry_point(): failed for plugname %s: %z", + plugname, result); + return result; + } + + if (version != SASL_CLIENT_PLUG_VERSION) + { + _sasl_log(NULL, SASL_LOG_WARN, + "version conflict in sasl_client_add_plugin for %s", plugname); + return SASL_BADVERS; + } + + for (lupe=0; lupe < plugcount; lupe++, pluglist++) + { + mech = sasl_ALLOC(sizeof(cmechanism_t)); + if (!mech) return SASL_NOMEM; + + mech->m.plug = pluglist; + if (_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) { + sasl_FREE(mech); + return SASL_NOMEM; + } + mech->m.version = version; + + /* sort mech_list by relative "strength" */ + mp = cmechlist->mech_list; + if (!mp || mech_compare(pluglist, mp->m.plug) >= 0) { + /* add mech to head of list */ + mech->next = cmechlist->mech_list; + cmechlist->mech_list = mech; + } else { + /* find where to insert mech into list */ + while (mp->next && + mech_compare(pluglist, mp->next->m.plug) <= 0) mp = mp->next; + mech->next = mp->next; + mp->next = mech; + } + + cmechlist->mech_length++; + } + + return SASL_OK; +} + +static int +client_idle(sasl_conn_t *conn) +{ + cmechanism_t *m; + if (! cmechlist) + return 0; + + for (m = cmechlist->mech_list; + m; + m = m->next) + if (m->m.plug->idle + && m->m.plug->idle(m->m.plug->glob_context, + conn, + conn ? ((sasl_client_conn_t *)conn)->cparams : NULL)) + return 1; + return 0; +} + +/* initialize the SASL client drivers + * callbacks -- base callbacks for all client connections + * returns: + * SASL_OK -- Success + * SASL_NOMEM -- Not enough memory + * SASL_BADVERS -- Mechanism version mismatch + * SASL_BADPARAM -- error in config file + * SASL_NOMECH -- No mechanisms available + * ... + */ + +int sasl_client_init(const sasl_callback_t *callbacks) +{ + int ret; + const add_plugin_list_t ep_list[] = { + { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin }, + { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin }, + { NULL, NULL } + }; + + /* lock allocation type */ + _sasl_allocation_locked++; + + if(_sasl_client_active) { + /* We're already active, just increase our refcount */ + /* xxx do something with the callback structure? */ + _sasl_client_active++; + return SASL_OK; + } + + global_callbacks_client.callbacks = callbacks; + global_callbacks_client.appname = NULL; + + cmechlist=sasl_ALLOC(sizeof(cmech_list_t)); + if (cmechlist==NULL) return SASL_NOMEM; + + /* We need to call client_done if we fail now */ + _sasl_client_active = 1; + + /* load plugins */ + ret=init_mechlist(); + if (ret!=SASL_OK) { + client_done(); + return ret; + } + + sasl_client_add_plugin("EXTERNAL", &external_client_plug_init); + + ret = _sasl_common_init(&global_callbacks_client); + + if (ret == SASL_OK) + ret = _sasl_load_plugins(ep_list, + _sasl_find_getpath_callback(callbacks), + _sasl_find_verifyfile_callback(callbacks)); + + if (ret == SASL_OK) { + _sasl_client_cleanup_hook = &client_done; + _sasl_client_idle_hook = &client_idle; + + ret = _sasl_build_mechlist(); + } else { + client_done(); + } + + return ret; +} + +static void client_dispose(sasl_conn_t *pconn) +{ + sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn; + + if (c_conn->mech && c_conn->mech->m.plug->mech_dispose) { + c_conn->mech->m.plug->mech_dispose(pconn->context, + c_conn->cparams->utils); + } + + pconn->context = NULL; + + if (c_conn->clientFQDN) + sasl_FREE(c_conn->clientFQDN); + + if (c_conn->cparams) { + _sasl_free_utils(&(c_conn->cparams->utils)); + sasl_FREE(c_conn->cparams); + } + + if (c_conn->mech_list != cmechlist->mech_list) { + /* free connection-specific mech_list */ + cmechanism_t *m, *prevm; + + m = c_conn->mech_list; /* m point to beginning of the list */ + + while (m) { + prevm = m; + m = m->next; + sasl_FREE(prevm); + } + } + + _sasl_conn_dispose(pconn); +} + +/* initialize a client exchange based on the specified mechanism + * service -- registered name of the service using SASL (e.g. "imap") + * serverFQDN -- the fully qualified domain name of the server + * iplocalport -- client IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * ipremoteport -- server IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * prompt_supp -- list of client interactions supported + * may also include sasl_getopt_t context & call + * NULL prompt_supp = user/pass via SASL_INTERACT only + * NULL proc = interaction supported via SASL_INTERACT + * secflags -- security flags (see above) + * in/out: + * pconn -- connection negotiation structure + * pointer to NULL => allocate new + * non-NULL => recycle storage and go for next available mech + * + * Returns: + * SASL_OK -- success + * SASL_NOMECH -- no mechanism meets requested properties + * SASL_NOMEM -- not enough memory + */ +int sasl_client_new(const char *service, + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *prompt_supp, + unsigned flags, + sasl_conn_t **pconn) +{ + int result; + char name[MAXFQDNLEN]; + sasl_client_conn_t *conn; + sasl_utils_t *utils; + sasl_getopt_t *getopt; + void *context; + const char *mlist = NULL; + int plus = 0; + + if (_sasl_client_active == 0) return SASL_NOTINIT; + + /* Remember, serverFQDN, iplocalport and ipremoteport can be NULL and be valid! */ + if (!pconn || !service) + return SASL_BADPARAM; + + *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t)); + if (*pconn==NULL) { + _sasl_log(NULL, SASL_LOG_ERR, + "Out of memory allocating connection context"); + return SASL_NOMEM; + } + memset(*pconn, 0, sizeof(sasl_client_conn_t)); + + (*pconn)->destroy_conn = &client_dispose; + + conn = (sasl_client_conn_t *)*pconn; + + conn->mech = NULL; + + conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t)); + if (conn->cparams==NULL) + MEMERROR(*pconn); + memset(conn->cparams,0,sizeof(sasl_client_params_t)); + + result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT, + &client_idle, serverFQDN, + iplocalport, ipremoteport, + prompt_supp, &global_callbacks_client); + if (result != SASL_OK) RETURN(*pconn, result); + + utils = _sasl_alloc_utils(*pconn, &global_callbacks_client); + if (utils == NULL) { + MEMERROR(*pconn); + } + + utils->conn= *pconn; + conn->cparams->utils = utils; + + if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + getopt(context, NULL, "client_mech_list", &mlist, NULL); + } + + /* if we have a client_mech_list, create ordered list of + available mechanisms for this conn */ + if (mlist) { + const char *cp; + cmechanism_t *mptr, *tail = NULL; + cmechanism_t *new; + + while (*mlist) { + /* find end of current mech name */ + for (cp = mlist; *cp && !isspace((int) *cp); cp++); + + /* search for mech name in loaded plugins */ + for (mptr = cmechlist->mech_list; mptr; mptr = mptr->next) { + const sasl_client_plug_t *plug = mptr->m.plug; + + if (_sasl_is_equal_mech(mlist, plug->mech_name, (size_t) (cp - mlist), &plus)) { + /* found a match */ + break; + } + } + if (mptr) { + new = sasl_ALLOC(sizeof(cmechanism_t)); + if (!new) { + result = SASL_NOMEM; + goto failed_client_new; + } + memcpy(&new->m, &mptr->m, sizeof(client_sasl_mechanism_t)); + new->next = NULL; + + if (!conn->mech_list) { + conn->mech_list = new; + tail = conn->mech_list; + } else { + if (tail) + tail->next = new; + tail = new; + } + conn->mech_length++; + } + + /* find next mech name */ + mlist = cp; + while (*mlist && isspace((int) *mlist)) mlist++; + } + } else { + conn->mech_list = cmechlist->mech_list; + conn->mech_length = cmechlist->mech_length; + } + + if (conn->mech_list == NULL) { + sasl_seterror(*pconn, 0, "No worthy mechs found"); + result = SASL_NOMECH; + goto failed_client_new; + } + + /* Setup the non-lazy parts of cparams, the rest is done in + * sasl_client_start */ + conn->cparams->canon_user = &_sasl_canon_user_lookup; + conn->cparams->flags = flags; + conn->cparams->prompt_supp = (*pconn)->callbacks; + + /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */ + memset(name, 0, sizeof(name)); + if (get_fqhostname (name, MAXFQDNLEN, 0) != 0) { + return (SASL_FAIL); + } + + result = _sasl_strdup(name, &conn->clientFQDN, NULL); + + if (result == SASL_OK) return SASL_OK; + +failed_client_new: + /* result isn't SASL_OK */ + _sasl_conn_dispose(*pconn); + sasl_FREE(*pconn); + *pconn = NULL; + _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new"); + return result; +} + +static int have_prompts(sasl_conn_t *conn, + const sasl_client_plug_t *mech) +{ + static const unsigned long default_prompts[] = { + SASL_CB_AUTHNAME, + SASL_CB_PASS, + SASL_CB_LIST_END + }; + + const unsigned long *prompt; + sasl_callback_ft pproc; + void *pcontext; + int result; + + for (prompt = (mech->required_prompts + ? mech->required_prompts : + default_prompts); + *prompt != SASL_CB_LIST_END; + prompt++) { + result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext); + if (result != SASL_OK && result != SASL_INTERACT) + return 0; /* we don't have this required prompt */ + } + + return 1; /* we have all the prompts */ +} + +static int +_mech_plus_p(const char *mech, size_t len) +{ + return (len > 5 && strncasecmp(&mech[len - 5], "-PLUS", 5) == 0); +} + +/* + * Order PLUS mechanisms first. Returns NUL separated list of + * *count items. + */ +static int +_sasl_client_order_mechs(const sasl_utils_t *utils, + const char *mechs, + int has_cb_data, + char **ordered_mechs, + size_t *count, + int *server_can_cb) +{ + char *list, *listp; + size_t i, mechslen, start; + + *count = 0; + *server_can_cb = 0; + + if (mechs == NULL || mechs[0] == '\0') + return SASL_NOMECH; + + mechslen = strlen(mechs); + + listp = list = utils->malloc(mechslen + 1); + if (list == NULL) + return SASL_NOMEM; + + /* As per RFC 4422: + * SASL mechanism allowable characters are "AZ-_" + * separators can be any other characters and of any length + * even variable lengths between. + * + * But for convenience we accept lowercase ASCII. + * + * Apps should be encouraged to simply use space or comma space + * though + */ +#define ismechchar(c) (isalnum((c)) || (c) == '_' || (c) == '-') + do { + for (i = start = 0; i <= mechslen; i++) { + if (!ismechchar(mechs[i])) { + const char *mechp = &mechs[start]; + size_t len = i - start; + + if (len != 0 && + _mech_plus_p(mechp, len) == has_cb_data) { + memcpy(listp, mechp, len); + listp[len] = '\0'; + listp += len + 1; + (*count)++; + if (*server_can_cb == 0 && has_cb_data) + *server_can_cb = 1; + } + start = i+1; + } + } + if (has_cb_data) + has_cb_data = 0; + else + break; + } while (1); + + if (*count == 0) { + utils->free(list); + return SASL_NOMECH; + } + + *ordered_mechs = list; + + return SASL_OK; +} + +static INLINE int +_sasl_cbinding_disp(sasl_client_params_t *cparams, + int mech_nego, + int server_can_cb, + sasl_cbinding_disp_t *cbindingdisp) +{ + /* + * If negotiating mechanisms, then we fail immediately if the + * client requires channel binding and the server does not + * advertise support. Otherwise we send "y" (which later will + * become "p" if we select a supporting mechanism). + * + * If the client explicitly selected a mechanism, then we only + * send channel bindings if they're marked critical. + */ + + *cbindingdisp = SASL_CB_DISP_NONE; + + if (SASL_CB_PRESENT(cparams)) { + if (mech_nego) { + if (!server_can_cb && SASL_CB_CRITICAL(cparams)) { + return SASL_NOMECH; + } else { + *cbindingdisp = SASL_CB_DISP_WANT; + } + } else if (SASL_CB_CRITICAL(cparams)) { + *cbindingdisp = SASL_CB_DISP_USED; + } + } + + return SASL_OK; +} + +/* select a mechanism for a connection + * mechlist -- mechanisms server has available (punctuation ignored) + * secret -- optional secret from previous session + * output: + * prompt_need -- on SASL_INTERACT, list of prompts needed to continue + * clientout -- the initial client response to send to the server + * mech -- set to mechanism name + * + * Returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + * SASL_NOMECH -- no mechanism meets requested properties + * SASL_INTERACT -- user interaction needed to fill in prompt_need list + */ + +/* + * SASL mechanism allowable characters are "AZ-_" + * separators can be any other characters and of any length + * even variable lengths between. + * + * But for convenience we accept lowercase ASCII. + * + * Apps should be encouraged to simply use space or comma space + * though + */ +int sasl_client_start(sasl_conn_t *conn, + const char *mechlist, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + const char **mech) +{ + sasl_client_conn_t *c_conn = (sasl_client_conn_t *) conn; + char *ordered_mechs = NULL, *name; + cmechanism_t *m = NULL, *bestm = NULL; + size_t i, list_len, name_len; + sasl_ssf_t minssf = 0; + int result, server_can_cb = 0; + sasl_cbinding_disp_t cbindingdisp; + sasl_cbinding_disp_t cur_cbindingdisp; + sasl_cbinding_disp_t best_cbindingdisp = SASL_CB_DISP_NONE; + + if (_sasl_client_active == 0) return SASL_NOTINIT; + + if (!conn) return SASL_BADPARAM; + + /* verify parameters */ + if (mechlist == NULL) { + PARAMERROR(conn); + } + + /* if prompt_need != NULL we've already been here + and just need to do the continue step again */ + + /* do a step */ + /* FIXME: Hopefully they only give us our own prompt_need back */ + if (prompt_need && *prompt_need != NULL) { + goto dostep; + } + + if (conn->props.min_ssf < conn->external.ssf) { + minssf = 0; + } else { + minssf = conn->props.min_ssf - conn->external.ssf; + } + + /* Order mechanisms so -PLUS are preferred */ + result = _sasl_client_order_mechs(c_conn->cparams->utils, + mechlist, + SASL_CB_PRESENT(c_conn->cparams), + &ordered_mechs, + &list_len, + &server_can_cb); + if (result != 0) + goto done; + + /* + * Determine channel binding disposition based on whether we + * are doing mechanism negotiation and whether server supports + * channel bindings. + */ + result = _sasl_cbinding_disp(c_conn->cparams, + (list_len > 1), + server_can_cb, + &cbindingdisp); + if (result != 0) + goto done; + + /* for each mechanism in client's list */ + for (m = c_conn->mech_list; !bestm && m != NULL; m = m->next) { + + for (i = 0, name = ordered_mechs; i < list_len; i++, name += name_len + 1) { + unsigned myflags; + int plus; + + name_len = strlen(name); + + if (!_sasl_is_equal_mech(name, m->m.plug->mech_name, name_len, &plus)) { + continue; + } + + /* Do we have the prompts for it? */ + if (!have_prompts(conn, m->m.plug)) + break; + + /* Is it strong enough? */ + if (minssf > m->m.plug->max_ssf) + break; + + myflags = conn->props.security_flags; + + /* if there's an external layer with a better SSF then this is no + * longer considered a plaintext mechanism + */ + if ((conn->props.min_ssf <= conn->external.ssf) && + (conn->external.ssf > 1)) { + myflags &= ~SASL_SEC_NOPLAINTEXT; + } + + /* Does it meet our security properties? */ + if (((myflags ^ m->m.plug->security_flags) & myflags) != 0) { + break; + } + + /* Can we meet it's features? */ + if (cbindingdisp == SASL_CB_DISP_USED && + !(m->m.plug->features & SASL_FEAT_CHANNEL_BINDING)) { + break; + } + + if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN) + && !conn->serverFQDN) { + break; + } + + /* Can it meet our features? */ + if ((conn->flags & SASL_NEED_PROXY) && + !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) { + break; + } + + if ((conn->flags & SASL_NEED_HTTP) && + !(m->m.plug->features & SASL_FEAT_SUPPORTS_HTTP)) { + break; + } + + if (SASL_CB_PRESENT(c_conn->cparams) && plus) { + cur_cbindingdisp = SASL_CB_DISP_USED; + } else { + cur_cbindingdisp = cbindingdisp; + } + + if (mech) { + *mech = m->m.plug->mech_name; + } + + /* Since the list of client mechs is ordered by preference/strength, + the first mech in our list that is available on the server and + meets our security properties and features is the "best" */ + best_cbindingdisp = cur_cbindingdisp; + bestm = m; + break; + } + } + + if (bestm == NULL) { + sasl_seterror(conn, 0, "No worthy mechs found"); + result = SASL_NOMECH; + goto done; + } + + /* make (the rest of) cparams */ + c_conn->cparams->service = conn->service; + c_conn->cparams->servicelen = (unsigned) strlen(conn->service); + + if (conn->serverFQDN) { + c_conn->cparams->serverFQDN = conn->serverFQDN; + c_conn->cparams->slen = (unsigned) strlen(conn->serverFQDN); + } + + c_conn->cparams->clientFQDN = c_conn->clientFQDN; + c_conn->cparams->clen = (unsigned) strlen(c_conn->clientFQDN); + + c_conn->cparams->external_ssf = conn->external.ssf; + c_conn->cparams->props = conn->props; + c_conn->cparams->cbindingdisp = best_cbindingdisp; + c_conn->mech = bestm; + + /* init that plugin */ + result = c_conn->mech->m.plug->mech_new(c_conn->mech->m.plug->glob_context, + c_conn->cparams, + &(conn->context)); + if (result != SASL_OK) goto done; + + /* do a step -- but only if we can do a client-send-first */ + dostep: + if(clientout) { + if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) { + *clientout = NULL; + *clientoutlen = 0; + result = SASL_CONTINUE; + } else { + result = sasl_client_step(conn, NULL, 0, prompt_need, + clientout, clientoutlen); + } + } + else + result = SASL_CONTINUE; + + done: + if (ordered_mechs != NULL) + c_conn->cparams->utils->free(ordered_mechs); + RETURN(conn, result); +} + +/* do a single authentication step. + * serverin -- the server message received by the client, MUST have a NUL + * sentinel, not counted by serverinlen + * output: + * prompt_need -- on SASL_INTERACT, list of prompts needed to continue + * clientout -- the client response to send to the server + * + * returns: + * SASL_OK -- success + * SASL_INTERACT -- user interaction needed to fill in prompt_need list + * SASL_BADPROT -- server protocol incorrect/cancelled + * SASL_BADSERV -- server failed mutual auth + */ + +int sasl_client_step(sasl_conn_t *conn, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen) +{ + sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; + int result; + + if (_sasl_client_active == 0) return SASL_NOTINIT; + if (!conn) return SASL_BADPARAM; + + /* check parameters */ + if ((serverin==NULL) && (serverinlen>0)) + PARAMERROR(conn); + + /* Don't do another step if the plugin told us that we're done */ + if (conn->oparams.doneflag) { + _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag"); + return SASL_FAIL; + } + + if(clientout) *clientout = NULL; + if(clientoutlen) *clientoutlen = 0; + + /* do a step */ + result = c_conn->mech->m.plug->mech_step(conn->context, + c_conn->cparams, + serverin, + serverinlen, + prompt_need, + clientout, clientoutlen, + &conn->oparams); + + if (result == SASL_OK) { + /* So we're done on this end, but if both + * 1. the mech does server-send-last + * 2. the protocol does not + * we need to return no data */ + if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) { + *clientout = ""; + *clientoutlen = 0; + } + + if(!conn->oparams.maxoutbuf) { + conn->oparams.maxoutbuf = conn->props.maxbufsize; + } + + if(conn->oparams.user == NULL || conn->oparams.authid == NULL) { + sasl_seterror(conn, 0, + "mech did not call canon_user for both authzid and authid"); + result = SASL_BADPROT; + } + } + + RETURN(conn,result); +} + +/* returns the length of all the mechanisms + * added up + */ + +static unsigned mech_names_len(cmechanism_t *mech_list) +{ + cmechanism_t *listptr; + unsigned result = 0; + + for (listptr = mech_list; + listptr; + listptr = listptr->next) + result += (unsigned) strlen(listptr->m.plug->mech_name); + + return result; +} + + +int _sasl_client_listmech(sasl_conn_t *conn, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount) +{ + sasl_client_conn_t *c_conn = (sasl_client_conn_t *)conn; + cmechanism_t *m = NULL; + sasl_ssf_t minssf = 0; + int ret; + size_t resultlen; + int flag; + const char *mysep; + + if (_sasl_client_active == 0) return SASL_NOTINIT; + if (!conn) return SASL_BADPARAM; + if (conn->type != SASL_CONN_CLIENT) PARAMERROR(conn); + + if (! result) + PARAMERROR(conn); + + if (plen != NULL) + *plen = 0; + if (pcount != NULL) + *pcount = 0; + + if (sep) { + mysep = sep; + } else { + mysep = " "; + } + + if (conn->props.min_ssf < conn->external.ssf) { + minssf = 0; + } else { + minssf = conn->props.min_ssf - conn->external.ssf; + } + + if (!c_conn->mech_list || c_conn->mech_length <= 0) { + INTERROR(conn, SASL_NOMECH); + } + + resultlen = (prefix ? strlen(prefix) : 0) + + (strlen(mysep) * (c_conn->mech_length - 1)) + + mech_names_len(c_conn->mech_list) + + (suffix ? strlen(suffix) : 0) + + 1; + ret = _buf_alloc(&conn->mechlist_buf, + &conn->mechlist_buf_len, + resultlen); + if (ret != SASL_OK) MEMERROR(conn); + + if (prefix) { + strcpy (conn->mechlist_buf,prefix); + } else { + *(conn->mechlist_buf) = '\0'; + } + + flag = 0; + for (m = c_conn->mech_list; m != NULL; m = m->next) { + /* do we have the prompts for it? */ + if (!have_prompts(conn, m->m.plug)) { + continue; + } + + /* is it strong enough? */ + if (minssf > m->m.plug->max_ssf) { + continue; + } + + /* does it meet our security properties? */ + if (((conn->props.security_flags ^ m->m.plug->security_flags) + & conn->props.security_flags) != 0) { + continue; + } + + /* Can we meet it's features? */ + if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN) + && !conn->serverFQDN) { + continue; + } + + /* Can it meet our features? */ + if ((conn->flags & SASL_NEED_PROXY) && + !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) { + continue; + } + + /* Okay, we like it, add it to the list! */ + + if (pcount != NULL) + (*pcount)++; + + /* print seperator */ + if (flag) { + strcat(conn->mechlist_buf, mysep); + } else { + flag = 1; + } + + /* now print the mechanism name */ + strcat(conn->mechlist_buf, m->m.plug->mech_name); + } + + if (suffix) + strcat(conn->mechlist_buf,suffix); + + if (plen!=NULL) + *plen = (unsigned) strlen(conn->mechlist_buf); + + *result = conn->mechlist_buf; + + return SASL_OK; +} + +sasl_string_list_t *_sasl_client_mechs(void) +{ + cmechanism_t *listptr; + sasl_string_list_t *retval = NULL, *next=NULL; + + if(!_sasl_client_active) return NULL; + + /* make list */ + for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) { + next = sasl_ALLOC(sizeof(sasl_string_list_t)); + + if(!next && !retval) return NULL; + else if(!next) { + next = retval->next; + do { + sasl_FREE(retval); + retval = next; + next = retval->next; + } while(next); + return NULL; + } + + next->d = listptr->m.plug->mech_name; + + if(!retval) { + next->next = NULL; + retval = next; + } else { + next->next = retval; + retval = next; + } + } + + return retval; +} + + + + +/* It would be nice if we can show other information like Author, Company, Year, plugin version */ +static void +_sasl_print_mechanism ( + client_sasl_mechanism_t *m, + sasl_info_callback_stage_t stage, + void *rock __attribute__((unused)) +) +{ + char delimiter; + + if (stage == SASL_INFO_LIST_START) { + printf ("List of client plugins follows\n"); + return; + } else if (stage == SASL_INFO_LIST_END) { + return; + } + + /* Process the mechanism */ + printf ("Plugin \"%s\" ", m->plugname); + + /* There is no delay loading for client side plugins */ + printf ("[loaded]"); + + printf (", \tAPI version: %d\n", m->version); + + if (m->plug != NULL) { + printf ("\tSASL mechanism: %s, best SSF: %d\n", + m->plug->mech_name, + m->plug->max_ssf); + + printf ("\tsecurity flags:"); + + delimiter = ' '; + if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) { + printf ("%cNO_ANONYMOUS", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) { + printf ("%cNO_PLAINTEXT", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NOACTIVE) { + printf ("%cNO_ACTIVE", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NODICTIONARY) { + printf ("%cNO_DICTIONARY", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) { + printf ("%cFORWARD_SECRECY", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) { + printf ("%cPASS_CREDENTIALS", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) { + printf ("%cMUTUAL_AUTH", delimiter); + delimiter = '|'; + } + + + + printf ("\n\tfeatures:"); + + delimiter = ' '; + if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) { + printf ("%cWANT_CLIENT_FIRST", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_SERVER_FIRST) { + printf ("%cSERVER_FIRST", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) { + printf ("%cPROXY_AUTHENTICATION", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) { + printf ("%cNEED_SERVER_FQDN", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_GSS_FRAMING) { + printf ("%cGSS_FRAMING", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) { + printf ("%cCHANNEL_BINDING", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_SUPPORTS_HTTP) { + printf ("%cSUPPORTS_HTTP", delimiter); + delimiter = '|'; + } + } + +/* Delay loading is not supported for the client side plugins: + if (m->f) { + printf ("\n\twill be loaded from \"%s\"", m->f); + } + */ + + printf ("\n"); +} + + +/* Dump information about available client plugins */ +int sasl_client_plugin_info ( + const char *c_mech_list, /* space separated mechanism list or NULL for ALL */ + sasl_client_info_callback_t *info_cb, + void *info_cb_rock +) +{ + cmechanism_t *m; + client_sasl_mechanism_t plug_data; + char * cur_mech; + char * mech_list = NULL; + char * p; + + if (info_cb == NULL) { + info_cb = _sasl_print_mechanism; + } + + if (cmechlist != NULL) { + info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock); + + if (c_mech_list == NULL) { + m = cmechlist->mech_list; /* m point to beginning of the list */ + + while (m != NULL) { + memcpy (&plug_data, &m->m, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + + m = m->next; + } + } else { + mech_list = strdup (c_mech_list); + + cur_mech = mech_list; + + while (cur_mech != NULL) { + p = strchr (cur_mech, ' '); + if (p != NULL) { + *p = '\0'; + p++; + } + + m = cmechlist->mech_list; /* m point to beginning of the list */ + + while (m != NULL) { + if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) { + memcpy (&plug_data, &m->m, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + } + + m = m->next; + } + + cur_mech = p; + } + + free (mech_list); + } + + info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock); + + return (SASL_OK); + } + + return (SASL_NOTINIT); +} diff --git a/contrib/libs/sasl/lib/common.c b/contrib/libs/sasl/lib/common.c new file mode 100644 index 0000000000..d9104c8956 --- /dev/null +++ b/contrib/libs/sasl/lib/common.c @@ -0,0 +1,2674 @@ +/* common.c - Functions that are common to server and clinet + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#ifdef HAVE_SYSLOG +#include <syslog.h> +#endif +#include <stdarg.h> +#include <ctype.h> +#include <assert.h> + +#include <sasl.h> +#include <saslutil.h> +#include <saslplug.h> +#include "saslint.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +static const char *implementation_string = "Cyrus SASL"; + +#define VSTR0(maj, min, step) #maj "." #min "." #step +#define VSTR(maj, min, step) VSTR0(maj, min, step) +#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \ + SASL_VERSION_STEP) + +static int _sasl_getpath(void *context __attribute__((unused)), const char **path); +static int _sasl_getpath_simple(void *context __attribute__((unused)), const char **path); +static int _sasl_getconfpath(void *context __attribute__((unused)), char ** path); +static int _sasl_getconfpath_simple(void *context __attribute__((unused)), const char **path); + +#if !defined(WIN32) +static char * _sasl_get_default_unix_path(void *context __attribute__((unused)), + char * env_var_name, char * default_value); +#else +/* NB: Always returned allocated value */ +static char * _sasl_get_default_win_path(void *context __attribute__((unused)), + TCHAR * reg_attr_name, char * default_value); +#endif + + +/* It turns out to be convenient to have a shared sasl_utils_t */ +const sasl_utils_t *sasl_global_utils = NULL; + +/* Should be a null-terminated array that lists the available mechanisms */ +static char **global_mech_list = NULL; + +void *free_mutex = NULL; + +int (*_sasl_client_cleanup_hook)(void) = NULL; +int (*_sasl_server_cleanup_hook)(void) = NULL; +int (*_sasl_client_idle_hook)(sasl_conn_t *conn) = NULL; +int (*_sasl_server_idle_hook)(sasl_conn_t *conn) = NULL; + +sasl_allocation_utils_t _sasl_allocation_utils={ + (sasl_malloc_t *) &malloc, + (sasl_calloc_t *) &calloc, + (sasl_realloc_t *) &realloc, + (sasl_free_t *) &free +}; +int _sasl_allocation_locked = 0; + +#define SASL_ENCODEV_EXTRA 4096 + +/* Default getpath/getconfpath callbacks. These can be edited by sasl_set_path(). */ +static sasl_callback_t default_getpath_cb = { + SASL_CB_GETPATH, (sasl_callback_ft)&_sasl_getpath, NULL +}; +static sasl_callback_t default_getconfpath_cb = { + SASL_CB_GETCONFPATH, (sasl_callback_ft)&_sasl_getconfpath, NULL +}; + +static char * default_plugin_path = NULL; +static char * default_conf_path = NULL; + +static int _sasl_global_getopt(void *context, + const char *plugin_name, + const char *option, + const char ** result, + unsigned *len); + +/* Intenal mutex functions do as little as possible (no thread protection) */ +static void *sasl_mutex_alloc(void) +{ + return (void *)0x1; +} + +static int sasl_mutex_lock(void *mutex __attribute__((unused))) +{ + return SASL_OK; +} + +static int sasl_mutex_unlock(void *mutex __attribute__((unused))) +{ + return SASL_OK; +} + +static void sasl_mutex_free(void *mutex __attribute__((unused))) +{ + return; +} + +sasl_mutex_utils_t _sasl_mutex_utils={ + &sasl_mutex_alloc, + &sasl_mutex_lock, + &sasl_mutex_unlock, + &sasl_mutex_free +}; + +void sasl_set_mutex(sasl_mutex_alloc_t *n, + sasl_mutex_lock_t *l, + sasl_mutex_unlock_t *u, + sasl_mutex_free_t *d) +{ + /* Disallow mutex function changes once sasl_client_init + and/or sasl_server_init is called */ + if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { + return; + } + + _sasl_mutex_utils.alloc=n; + _sasl_mutex_utils.lock=l; + _sasl_mutex_utils.unlock=u; + _sasl_mutex_utils.free=d; +} + +/* copy a string to malloced memory */ +int _sasl_strdup(const char *in, char **out, size_t *outlen) +{ + size_t len = strlen(in); + if (outlen) *outlen = len; + *out=sasl_ALLOC((unsigned) len + 1); + if (! *out) return SASL_NOMEM; + strcpy((char *) *out, in); + return SASL_OK; +} + +/* adds a string to the buffer; reallocing if need be */ +int _sasl_add_string(char **out, size_t *alloclen, + size_t *outlen, const char *add) +{ + size_t addlen; + + if (add==NULL) add = "(null)"; + + addlen=strlen(add); /* only compute once */ + if (_buf_alloc(out, alloclen, (*outlen)+addlen+1)!=SASL_OK) + return SASL_NOMEM; + + strcpy(*out + *outlen, add); + *outlen += addlen; + + return SASL_OK; +} + +/* a simpler way to set plugin path or configuration file path + * without the need to set sasl_getpath_t callback. + * + * This function can be called before sasl_server_init/sasl_client_init. + * + * Don't call this function without locking in a multithreaded application. + */ +int sasl_set_path (int path_type, char * path) +{ + int result; + + if (path == NULL) { + return (SASL_FAIL); + } + + switch (path_type) { + case SASL_PATH_TYPE_PLUGIN: + if (default_plugin_path != NULL) { + sasl_FREE (default_plugin_path); + default_plugin_path = NULL; + } + result = _sasl_strdup (path, &default_plugin_path, NULL); + if (result != SASL_OK) { + return (result); + } + + /* Update the default getpath_t callback */ + default_getpath_cb.proc = (sasl_callback_ft)&_sasl_getpath_simple; + break; + + case SASL_PATH_TYPE_CONFIG: + if (default_conf_path != NULL) { + sasl_FREE (default_conf_path); + default_conf_path = NULL; + } + result = _sasl_strdup (path, &default_conf_path, NULL); + if (result != SASL_OK) { + return (result); + } + + /* Update the default getpath_t callback */ + default_getconfpath_cb.proc = (sasl_callback_ft)&_sasl_getconfpath_simple; + break; + + default: + return (SASL_FAIL); + } + + return (SASL_OK); +} + +/* return the version of the cyrus sasl library as compiled, + * using 32 bits: high byte is major version, second byte is minor version, + * low 16 bits are step #. + * Patch version is not available using this function, + * use sasl_version_info() instead. + */ +void sasl_version(const char **implementation, int *version) +{ + if(implementation) *implementation = implementation_string; + /* NB: the format is not the same as in SASL_VERSION_FULL */ + if(version) *version = (SASL_VERSION_MAJOR << 24) | + (SASL_VERSION_MINOR << 16) | + (SASL_VERSION_STEP); +} + +/* Extended version of sasl_version above */ +void sasl_version_info (const char **implementation, const char **version_string, + int *version_major, int *version_minor, int *version_step, + int *version_patch) +{ + if (implementation) *implementation = implementation_string; + if (version_string) *version_string = SASL_VERSION_STRING; + if (version_major) *version_major = SASL_VERSION_MAJOR; + if (version_minor) *version_minor = SASL_VERSION_MINOR; + if (version_step) *version_step = SASL_VERSION_STEP; + /* Version patch is always 0 for CMU SASL */ + if (version_patch) *version_patch = 0; +} + +/* security-encode a regular string. Mostly a wrapper for sasl_encodev */ +/* output is only valid until next call to sasl_encode or sasl_encodev */ +int sasl_encode(sasl_conn_t *conn, const char *input, + unsigned inputlen, + const char **output, unsigned *outputlen) +{ + int result; + struct iovec tmp; + + if(!conn) return SASL_BADPARAM; + if(!input || !inputlen || !output || !outputlen) + PARAMERROR(conn); + + /* maxoutbuf checking is done in sasl_encodev */ + + /* Note: We are casting a const pointer here, but it's okay + * because we believe people downstream of us are well-behaved, and the + * alternative is an absolute mess, performance-wise. */ + tmp.iov_base = (void *)input; + tmp.iov_len = inputlen; + + result = sasl_encodev(conn, &tmp, 1, output, outputlen); + + RETURN(conn, result); +} + +/* Internal function that doesn't do any verification */ +static int +_sasl_encodev (sasl_conn_t *conn, + const struct iovec *invec, + unsigned numiov, + int * p_num_packets, /* number of packets generated so far */ + const char **output, /* previous output, if *p_num_packets > 0 */ + unsigned *outputlen) +{ + int result; + char * new_buf; + + assert (conn->oparams.encode != NULL); + + if (*p_num_packets == 1) { + /* This is the second call to this function, + so we need to allocate a new output buffer + and copy existing data there. */ + conn->multipacket_encoded_data.curlen = *outputlen; + if (conn->multipacket_encoded_data.data == NULL) { + conn->multipacket_encoded_data.reallen = + conn->multipacket_encoded_data.curlen + SASL_ENCODEV_EXTRA; + conn->multipacket_encoded_data.data = + sasl_ALLOC(conn->multipacket_encoded_data.reallen + 1); + + if (conn->multipacket_encoded_data.data == NULL) { + MEMERROR(conn); + } + } else { + /* A buffer left from a previous sasl_encodev call. + Make sure it is big enough. */ + if (conn->multipacket_encoded_data.curlen > + conn->multipacket_encoded_data.reallen) { + conn->multipacket_encoded_data.reallen = + conn->multipacket_encoded_data.curlen + SASL_ENCODEV_EXTRA; + + new_buf = sasl_REALLOC(conn->multipacket_encoded_data.data, + conn->multipacket_encoded_data.reallen + 1); + if (new_buf == NULL) { + MEMERROR(conn); + } + conn->multipacket_encoded_data.data = new_buf; + } + } + + memcpy (conn->multipacket_encoded_data.data, + *output, + *outputlen); + } + + result = conn->oparams.encode(conn->context, + invec, + numiov, + output, + outputlen); + + if (*p_num_packets > 0 && result == SASL_OK) { + /* Is the allocated buffer big enough? If not, grow it. */ + if ((conn->multipacket_encoded_data.curlen + *outputlen) > + conn->multipacket_encoded_data.reallen) { + conn->multipacket_encoded_data.reallen = + conn->multipacket_encoded_data.curlen + *outputlen; + new_buf = sasl_REALLOC(conn->multipacket_encoded_data.data, + conn->multipacket_encoded_data.reallen + 1); + if (new_buf == NULL) { + MEMERROR(conn); + } + conn->multipacket_encoded_data.data = new_buf; + } + + /* Append new data to the end of the buffer */ + memcpy (conn->multipacket_encoded_data.data + + conn->multipacket_encoded_data.curlen, + *output, + *outputlen); + conn->multipacket_encoded_data.curlen += *outputlen; + + *output = conn->multipacket_encoded_data.data; + *outputlen = (unsigned)conn->multipacket_encoded_data.curlen; + } + + (*p_num_packets)++; + + RETURN(conn, result); +} + +/* security-encode an iovec */ +/* output is only valid until the next call to sasl_encode or sasl_encodev */ +int sasl_encodev(sasl_conn_t *conn, + const struct iovec *invec, + unsigned numiov, + const char **output, + unsigned *outputlen) +{ + int result = SASL_OK; + unsigned i; + unsigned j; + size_t total_size = 0; + struct iovec *cur_invec = NULL; + struct iovec last_invec; + unsigned cur_numiov; + char * next_buf = NULL; + size_t remainder_len; + unsigned index_offset; + unsigned allocated = 0; + /* Number of generated SASL packets */ + int num_packets = 0; + + if (!conn) return SASL_BADPARAM; + if (! invec || ! output || ! outputlen || numiov < 1) { + PARAMERROR(conn); + } + + if (!conn->props.maxbufsize) { + sasl_seterror(conn, 0, + "called sasl_encode[v] with application that does not support security layers"); + return SASL_TOOWEAK; + } + + /* If oparams.encode is NULL, this means there is no SASL security + layer in effect, so no SASL framing is needed. */ + if (conn->oparams.encode == NULL) { + result = _iovec_to_buf(invec, numiov, &conn->encode_buf); + if (result != SASL_OK) INTERROR(conn, result); + + *output = conn->encode_buf->data; + *outputlen = (unsigned) conn->encode_buf->curlen; + + RETURN(conn, result); + } + + /* This might be better to check on a per-plugin basis, but I think + * it's cleaner and more effective here. It also encourages plugins + * to be honest about what they accept */ + + last_invec.iov_base = NULL; + remainder_len = 0; + next_buf = NULL; + i = 0; + while (i < numiov) { + if ((total_size + invec[i].iov_len) > conn->oparams.maxoutbuf) { + + /* CLAIM: total_size < conn->oparams.maxoutbuf */ + + /* Fit as many bytes in last_invec, so that we have conn->oparams.maxoutbuf + bytes in total. */ + last_invec.iov_len = conn->oparams.maxoutbuf - total_size; + /* Point to the first byte of the current record. */ + last_invec.iov_base = invec[i].iov_base; + + /* Note that total_size < conn->oparams.maxoutbuf */ + /* The total size of the iov is bigger then the other end can accept. + So we allocate a new iov that contains just enough. */ + + /* +1 --- for the tail record */ + cur_numiov = i + 1; + + /* +1 --- just in case we need the head record */ + if ((cur_numiov + 1) > allocated) { + struct iovec *new_invec; + + allocated = cur_numiov + 1; + new_invec = sasl_REALLOC (cur_invec, sizeof(struct iovec) * allocated); + if (new_invec == NULL) { + if (cur_invec != NULL) { + sasl_FREE(cur_invec); + } + MEMERROR(conn); + } + cur_invec = new_invec; + } + + if (next_buf != NULL) { + cur_invec[0].iov_base = next_buf; + cur_invec[0].iov_len = (long)remainder_len; + cur_numiov++; + index_offset = 1; + } else { + index_offset = 0; + } + + if (i > 0) { + /* Copy all previous chunks */ + /* NOTE - The starting index in invec is always 0 */ + for (j = 0; j < i; j++) { + cur_invec[j + index_offset] = invec[j]; + } + } + + /* Initialize the last record */ + cur_invec[i + index_offset] = last_invec; + + result = _sasl_encodev (conn, + cur_invec, + cur_numiov, + &num_packets, + output, + outputlen); + + if (result != SASL_OK) { + goto cleanup; + } + + /* Point to the first byte that wouldn't fit into + the conn->oparams.maxoutbuf buffer. */ + /* Note, if next_buf points to the very end of the IOV record, + it will be reset to NULL below */ + /* Note, that some platforms define iov_base as "void *", + thus the typecase below */ + next_buf = (char *) last_invec.iov_base + last_invec.iov_len; + /* Note - remainder_len is how many bytes left to be encoded in + the current IOV slot. */ + remainder_len = (total_size + invec[i].iov_len) - conn->oparams.maxoutbuf; + + /* Skip all consumed IOV records */ + invec += i + 1; + numiov = numiov - (i + 1); + i = 0; + + while (remainder_len > conn->oparams.maxoutbuf) { + last_invec.iov_base = next_buf; + last_invec.iov_len = conn->oparams.maxoutbuf; + + /* Note, if next_buf points to the very end of the IOV record, + it will be reset to NULL below */ + /* Note, that some platforms define iov_base as "void *", + thus the typecase below */ + next_buf = (char *) last_invec.iov_base + last_invec.iov_len; + remainder_len = remainder_len - conn->oparams.maxoutbuf; + + result = _sasl_encodev (conn, + &last_invec, + 1, + &num_packets, + output, + outputlen); + if (result != SASL_OK) { + goto cleanup; + } + } + + total_size = remainder_len; + + if (remainder_len == 0) { + /* Just clear next_buf */ + next_buf = NULL; + } + } else { + total_size += invec[i].iov_len; + i++; + } + } + + /* CLAIM - The remaining data is shorter then conn->oparams.maxoutbuf. */ + + /* Force encoding of any partial buffer. Might not be optimal on the wire. */ + if (next_buf != NULL) { + last_invec.iov_base = next_buf; + last_invec.iov_len = (long)remainder_len; + + result = _sasl_encodev (conn, + &last_invec, + 1, + &num_packets, + output, + outputlen); + + if (result != SASL_OK) { + goto cleanup; + } + } + + if (numiov > 0) { + result = _sasl_encodev (conn, + invec, + numiov, + &num_packets, + output, + outputlen); + } + +cleanup: + if (cur_invec != NULL) { + sasl_FREE(cur_invec); + } + + RETURN(conn, result); +} + +/* output is only valid until next call to sasl_decode */ +int sasl_decode(sasl_conn_t *conn, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen) +{ + int result; + + if(!conn) return SASL_BADPARAM; + if(!input || !output || !outputlen) + PARAMERROR(conn); + + if(!conn->props.maxbufsize) { + sasl_seterror(conn, 0, + "called sasl_decode with application that does not support security layers"); + RETURN(conn, SASL_TOOWEAK); + } + + if(conn->oparams.decode == NULL) + { + /* Since we know how long the output is maximally, we can + * just allocate it to begin with, and never need another + * allocation! */ + + /* However, if they pass us more than they actually can take, + * we cannot help them... */ + if(inputlen > conn->props.maxbufsize) { + sasl_seterror(conn, 0, + "input too large for default sasl_decode"); + RETURN(conn,SASL_BUFOVER); + } + + if(!conn->decode_buf) + conn->decode_buf = sasl_ALLOC(conn->props.maxbufsize + 1); + if(!conn->decode_buf) + MEMERROR(conn); + + memcpy(conn->decode_buf, input, inputlen); + conn->decode_buf[inputlen] = '\0'; + *output = conn->decode_buf; + *outputlen = inputlen; + + return SASL_OK; + } else { + result = conn->oparams.decode(conn->context, input, inputlen, + output, outputlen); + + /* NULL an empty buffer (for misbehaved applications) */ + if (*outputlen == 0) *output = NULL; + + RETURN(conn, result); + } + + INTERROR(conn, SASL_FAIL); +} + + +void +sasl_set_alloc(sasl_malloc_t *m, + sasl_calloc_t *c, + sasl_realloc_t *r, + sasl_free_t *f) +{ + if (_sasl_allocation_locked++) return; + + _sasl_allocation_utils.malloc=m; + _sasl_allocation_utils.calloc=c; + _sasl_allocation_utils.realloc=r; + _sasl_allocation_utils.free=f; +} + +void sasl_common_done(void) +{ + /* NOTE - the caller will need to reinitialize the values, + if it is going to call sasl_client_init/sasl_server_init again. */ + if (default_plugin_path != NULL) { + sasl_FREE (default_plugin_path); + default_plugin_path = NULL; + } + if (default_conf_path != NULL) { + sasl_FREE (default_conf_path); + default_conf_path = NULL; + } + + _sasl_canonuser_free(); + _sasl_done_with_plugins(); + + sasl_MUTEX_FREE(free_mutex); + free_mutex = NULL; + + _sasl_free_utils(&sasl_global_utils); + + if (global_mech_list) { + sasl_FREE(global_mech_list); + global_mech_list = NULL; + } +} + +/* This function is for backward compatibility */ +void sasl_done(void) +{ + if (_sasl_server_cleanup_hook && _sasl_server_cleanup_hook() == SASL_OK) { + _sasl_server_idle_hook = NULL; + _sasl_server_cleanup_hook = NULL; + } + + if (_sasl_client_cleanup_hook && _sasl_client_cleanup_hook() == SASL_OK) { + _sasl_client_idle_hook = NULL; + _sasl_client_cleanup_hook = NULL; + } + + if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { + return; + } + + sasl_common_done(); +} + +/* fills in the base sasl_conn_t info */ +int _sasl_conn_init(sasl_conn_t *conn, + const char *service, + unsigned int flags, + enum Sasl_conn_type type, + int (*idle_hook)(sasl_conn_t *conn), + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *callbacks, + const sasl_global_callbacks_t *global_callbacks) { + int result = SASL_OK; + + conn->type = type; + + result = _sasl_strdup(service, &conn->service, NULL); + if (result != SASL_OK) + MEMERROR(conn); + + memset(&conn->oparams, 0, sizeof(sasl_out_params_t)); + memset(&conn->external, 0, sizeof(_sasl_external_properties_t)); + + conn->flags = flags; + + result = sasl_setprop(conn, SASL_IPLOCALPORT, iplocalport); + if(result != SASL_OK) + RETURN(conn, result); + + result = sasl_setprop(conn, SASL_IPREMOTEPORT, ipremoteport); + if(result != SASL_OK) + RETURN(conn, result); + + conn->encode_buf = NULL; + conn->context = NULL; + conn->secret = NULL; + conn->idle_hook = idle_hook; + conn->callbacks = callbacks; + conn->global_callbacks = global_callbacks; + + memset(&conn->props, 0, sizeof(conn->props)); + + /* Start this buffer out as an empty string */ + conn->error_code = SASL_OK; + conn->errdetail_buf = conn->error_buf = NULL; + conn->errdetail_buf_len = conn->error_buf_len = 150; + + result = _buf_alloc(&conn->error_buf, &conn->error_buf_len, 150); + if(result != SASL_OK) MEMERROR(conn); + result = _buf_alloc(&conn->errdetail_buf, &conn->errdetail_buf_len, 150); + if(result != SASL_OK) MEMERROR(conn); + + conn->error_buf[0] = '\0'; + conn->errdetail_buf[0] = '\0'; + + conn->decode_buf = NULL; + + if(serverFQDN) { + result = _sasl_strdup(serverFQDN, &conn->serverFQDN, NULL); + sasl_strlower (conn->serverFQDN); + } else if (conn->type == SASL_CONN_SERVER) { + /* We can fake it because we *are* the server */ + char name[MAXFQDNLEN]; + memset(name, 0, sizeof(name)); + if (get_fqhostname (name, MAXFQDNLEN, 0) != 0) { + return (SASL_FAIL); + } + + result = _sasl_strdup(name, &conn->serverFQDN, NULL); + } else { + conn->serverFQDN = NULL; + } + + + if(result != SASL_OK) MEMERROR( conn ); + + RETURN(conn, SASL_OK); +} + +int _sasl_common_init(sasl_global_callbacks_t *global_callbacks) +{ + int result; + + /* The last specified global callback always wins */ + if (sasl_global_utils != NULL) { + sasl_utils_t * global_utils = (sasl_utils_t *)sasl_global_utils; + global_utils->getopt = &_sasl_global_getopt; + global_utils->getopt_context = global_callbacks; + } + + /* Do nothing if we are already initialized */ + if (free_mutex) { + return SASL_OK; + } + + /* Setup the global utilities */ + if(!sasl_global_utils) { + sasl_global_utils = _sasl_alloc_utils(NULL, global_callbacks); + if(sasl_global_utils == NULL) return SASL_NOMEM; + } + + /* Init the canon_user plugin */ + result = sasl_canonuser_add_plugin("INTERNAL", internal_canonuser_init); + if(result != SASL_OK) return result; + + if (!free_mutex) { + free_mutex = sasl_MUTEX_ALLOC(); + } + if (!free_mutex) return SASL_FAIL; + + return SASL_OK; +} + +/* dispose connection state, sets it to NULL + * checks for pointer to NULL + */ +void sasl_dispose(sasl_conn_t **pconn) +{ + int result; + + if (! pconn) return; + if (! *pconn) return; + + /* serialize disposes. this is necessary because we can't + dispose of conn->mutex if someone else is locked on it */ + if (!free_mutex) { + free_mutex = sasl_MUTEX_ALLOC(); + if (!free_mutex) return; + } + + result = sasl_MUTEX_LOCK(free_mutex); + if (result!=SASL_OK) return; + + /* *pconn might have become NULL by now */ + if (*pconn) { + (*pconn)->destroy_conn(*pconn); + sasl_FREE(*pconn); + *pconn=NULL; + } + + sasl_MUTEX_UNLOCK(free_mutex); +} + +void _sasl_conn_dispose(sasl_conn_t *conn) { + if (conn->serverFQDN) + sasl_FREE(conn->serverFQDN); + + if (conn->external.auth_id) + sasl_FREE(conn->external.auth_id); + + if(conn->encode_buf) { + if(conn->encode_buf->data) sasl_FREE(conn->encode_buf->data); + sasl_FREE(conn->encode_buf); + } + + if(conn->error_buf) + sasl_FREE(conn->error_buf); + + if(conn->errdetail_buf) + sasl_FREE(conn->errdetail_buf); + + if(conn->decode_buf) + sasl_FREE(conn->decode_buf); + + if(conn->mechlist_buf) + sasl_FREE(conn->mechlist_buf); + + if(conn->service) + sasl_FREE(conn->service); + + if (conn->multipacket_encoded_data.data) { + sasl_FREE(conn->multipacket_encoded_data.data); + } + + /* oparams sub-members should be freed by the plugin, in so much + * as they were allocated by the plugin */ +} + + +/* get property from SASL connection state + * propnum -- property number + * pvalue -- pointer to value + * returns: + * SASL_OK -- no error + * SASL_NOTDONE -- property not available yet + * SASL_BADPARAM -- bad property number or SASL context is NULL + */ +int sasl_getprop(sasl_conn_t *conn, int propnum, const void **pvalue) +{ + int result = SASL_OK; + sasl_getopt_t *getopt; + void *context; + + if (! conn) return SASL_BADPARAM; + if (! pvalue) PARAMERROR(conn); + + switch(propnum) + { + case SASL_SSF: + *(sasl_ssf_t **)pvalue= &conn->oparams.mech_ssf; + break; + case SASL_MAXOUTBUF: + *(unsigned **)pvalue = &conn->oparams.maxoutbuf; + break; + case SASL_GETOPTCTX: + result = _sasl_getcallback(conn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context); + if(result != SASL_OK) break; + + *(void **)pvalue = context; + break; + case SASL_CALLBACK: + *(const sasl_callback_t **)pvalue = conn->callbacks; + break; + case SASL_IPLOCALPORT: + if(conn->got_ip_local) + *(const char **)pvalue = conn->iplocalport; + else { + *(const char **)pvalue = NULL; + result = SASL_NOTDONE; + } + break; + case SASL_IPREMOTEPORT: + if(conn->got_ip_remote) + *(const char **)pvalue = conn->ipremoteport; + else { + *(const char **)pvalue = NULL; + result = SASL_NOTDONE; + } + break; + case SASL_USERNAME: + if(! conn->oparams.user) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.user; + break; + case SASL_AUTHUSER: + if(! conn->oparams.authid) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.authid; + break; + case SASL_APPNAME: + /* Currently we only support server side contexts, but we should + be able to extend this to support client side contexts as well */ + if(conn->type != SASL_CONN_SERVER) result = SASL_BADPROT; + else + *((const char **)pvalue) = ((sasl_server_conn_t *)conn)->sparams->appname; + break; + case SASL_SERVERFQDN: + *((const char **)pvalue) = conn->serverFQDN; + break; + case SASL_DEFUSERREALM: + if(conn->type != SASL_CONN_SERVER) result = SASL_BADPROT; + else + *((const char **)pvalue) = ((sasl_server_conn_t *)conn)->user_realm; + break; + case SASL_SERVICE: + *((const char **)pvalue) = conn->service; + break; + case SASL_AUTHSOURCE: /* name of plugin (not name of mech) */ + if(conn->type == SASL_CONN_CLIENT) { + if(!((sasl_client_conn_t *)conn)->mech) { + result = SASL_NOTDONE; + break; + } + *((const char **)pvalue) = + ((sasl_client_conn_t *)conn)->mech->m.plugname; + } else if (conn->type == SASL_CONN_SERVER) { + if(!((sasl_server_conn_t *)conn)->mech) { + result = SASL_NOTDONE; + break; + } + *((const char **)pvalue) = + ((sasl_server_conn_t *)conn)->mech->m.plugname; + } else { + result = SASL_BADPARAM; + } + break; + case SASL_MECHNAME: /* name of mech */ + if(conn->type == SASL_CONN_CLIENT) { + if(!((sasl_client_conn_t *)conn)->mech) { + result = SASL_NOTDONE; + break; + } + *((const char **)pvalue) = + ((sasl_client_conn_t *)conn)->mech->m.plug->mech_name; + } else if (conn->type == SASL_CONN_SERVER) { + if(!((sasl_server_conn_t *)conn)->mech) { + result = SASL_NOTDONE; + break; + } + *((const char **)pvalue) = + ((sasl_server_conn_t *)conn)->mech->m.plug->mech_name; + } else { + result = SASL_BADPARAM; + } + + if(!(*pvalue) && result == SASL_OK) result = SASL_NOTDONE; + break; + case SASL_PLUGERR: + *((const char **)pvalue) = conn->error_buf; + break; + case SASL_DELEGATEDCREDS: + /* We can't really distinguish between "no delegated credentials" + and "authentication not finished" */ + if(! conn->oparams.client_creds) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.client_creds; + break; + case SASL_GSS_PEER_NAME: + if(! conn->oparams.gss_peer_name) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.gss_peer_name; + break; + case SASL_GSS_LOCAL_NAME: + if(! conn->oparams.gss_local_name) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.gss_local_name; + break; + case SASL_SSF_EXTERNAL: + *((const sasl_ssf_t **)pvalue) = &conn->external.ssf; + break; + case SASL_AUTH_EXTERNAL: + *((const char **)pvalue) = conn->external.auth_id; + break; + case SASL_SEC_PROPS: + *((const sasl_security_properties_t **)pvalue) = &conn->props; + break; + case SASL_GSS_CREDS: + if(conn->type == SASL_CONN_CLIENT) + *(const void **)pvalue = + ((sasl_client_conn_t *)conn)->cparams->gss_creds; + else + *(const void **)pvalue = + ((sasl_server_conn_t *)conn)->sparams->gss_creds; + break; + case SASL_HTTP_REQUEST: { + if (conn->type == SASL_CONN_SERVER) + *(const sasl_http_request_t **)pvalue = + ((sasl_server_conn_t *)conn)->sparams->http_request; + else + *(const sasl_http_request_t **)pvalue = + ((sasl_client_conn_t *)conn)->cparams->http_request; + break; + } + default: + result = SASL_BADPARAM; + } + + if(result == SASL_BADPARAM) { + PARAMERROR(conn); + } else if(result == SASL_NOTDONE) { + sasl_seterror(conn, SASL_NOLOG, + "Information that was requested is not yet available."); + RETURN(conn, result); + } else if(result != SASL_OK) { + INTERROR(conn, result); + } else + RETURN(conn, result); +} + +/* set property in SASL connection state + * returns: + * SASL_OK -- value set + * SASL_BADPARAM -- invalid property or value + */ +int sasl_setprop(sasl_conn_t *conn, int propnum, const void *value) +{ + int result = SASL_OK; + char *str; + + /* make sure the sasl context is valid */ + if (!conn) + return SASL_BADPARAM; + + switch(propnum) + { + case SASL_SSF_EXTERNAL: + conn->external.ssf = *((sasl_ssf_t *)value); + if(conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t*)conn)->sparams->external_ssf = + conn->external.ssf; + } else { + ((sasl_client_conn_t*)conn)->cparams->external_ssf = + conn->external.ssf; + } + break; + + case SASL_AUTH_EXTERNAL: + if(value && strlen(value)) { + result = _sasl_strdup(value, &str, NULL); + if(result != SASL_OK) MEMERROR(conn); + } else { + str = NULL; + } + + if(conn->external.auth_id) + sasl_FREE(conn->external.auth_id); + + conn->external.auth_id = str; + + break; + + case SASL_DEFUSERREALM: + if(conn->type != SASL_CONN_SERVER) { + sasl_seterror(conn, 0, "Tried to set realm on non-server connection"); + result = SASL_BADPROT; + break; + } + + if(value && strlen(value)) { + result = _sasl_strdup(value, &str, NULL); + if(result != SASL_OK) MEMERROR(conn); + } else { + PARAMERROR(conn); + } + + if(((sasl_server_conn_t *)conn)->user_realm) + sasl_FREE(((sasl_server_conn_t *)conn)->user_realm); + + ((sasl_server_conn_t *)conn)->user_realm = str; + ((sasl_server_conn_t *)conn)->sparams->user_realm = str; + + break; + + case SASL_SEC_PROPS: + { + sasl_security_properties_t *props = (sasl_security_properties_t *)value; + + if(props->maxbufsize == 0 && props->min_ssf != 0) { + sasl_seterror(conn, 0, + "Attempt to disable security layers (maxoutbuf == 0) with min_ssf > 0"); + RETURN(conn, SASL_TOOWEAK); + } + + conn->props = *props; + + if(conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t*)conn)->sparams->props = *props; + } else { + ((sasl_client_conn_t*)conn)->cparams->props = *props; + } + + break; + } + + case SASL_IPREMOTEPORT: + { + const char *ipremoteport = (const char *)value; + if(!value) { + conn->got_ip_remote = 0; + } else if (_sasl_ipfromstring(ipremoteport, NULL, 0) + != SASL_OK) { + sasl_seterror(conn, 0, "Bad IPREMOTEPORT value"); + RETURN(conn, SASL_BADPARAM); + } else { + strcpy(conn->ipremoteport, ipremoteport); + conn->got_ip_remote = 1; + } + + if(conn->got_ip_remote) { + if(conn->type == SASL_CONN_CLIENT) { + ((sasl_client_conn_t *)conn)->cparams->ipremoteport + = conn->ipremoteport; + ((sasl_client_conn_t *)conn)->cparams->ipremlen = + (unsigned) strlen(conn->ipremoteport); + } else if (conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t *)conn)->sparams->ipremoteport + = conn->ipremoteport; + ((sasl_server_conn_t *)conn)->sparams->ipremlen = + (unsigned) strlen(conn->ipremoteport); + } + } else { + if(conn->type == SASL_CONN_CLIENT) { + ((sasl_client_conn_t *)conn)->cparams->ipremoteport + = NULL; + ((sasl_client_conn_t *)conn)->cparams->ipremlen = 0; + } else if (conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t *)conn)->sparams->ipremoteport + = NULL; + ((sasl_server_conn_t *)conn)->sparams->ipremlen = 0; + } + } + + break; + } + + case SASL_IPLOCALPORT: + { + const char *iplocalport = (const char *)value; + if(!value) { + conn->got_ip_local = 0; + } else if (_sasl_ipfromstring(iplocalport, NULL, 0) + != SASL_OK) { + sasl_seterror(conn, 0, "Bad IPLOCALPORT value"); + RETURN(conn, SASL_BADPARAM); + } else { + strcpy(conn->iplocalport, iplocalport); + conn->got_ip_local = 1; + } + + if(conn->got_ip_local) { + if(conn->type == SASL_CONN_CLIENT) { + ((sasl_client_conn_t *)conn)->cparams->iplocalport + = conn->iplocalport; + ((sasl_client_conn_t *)conn)->cparams->iploclen + = (unsigned) strlen(conn->iplocalport); + } else if (conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t *)conn)->sparams->iplocalport + = conn->iplocalport; + ((sasl_server_conn_t *)conn)->sparams->iploclen + = (unsigned) strlen(conn->iplocalport); + } + } else { + if(conn->type == SASL_CONN_CLIENT) { + ((sasl_client_conn_t *)conn)->cparams->iplocalport + = NULL; + ((sasl_client_conn_t *)conn)->cparams->iploclen = 0; + } else if (conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t *)conn)->sparams->iplocalport + = NULL; + ((sasl_server_conn_t *)conn)->sparams->iploclen = 0; + } + } + break; + } + + case SASL_APPNAME: + /* Currently we only support server side contexts, but we should + be able to extend this to support client side contexts as well */ + if(conn->type != SASL_CONN_SERVER) { + sasl_seterror(conn, 0, "Tried to set application name on non-server connection"); + result = SASL_BADPROT; + break; + } + + if(((sasl_server_conn_t *)conn)->appname) { + sasl_FREE(((sasl_server_conn_t *)conn)->appname); + ((sasl_server_conn_t *)conn)->appname = NULL; + } + + if(value && strlen(value)) { + result = _sasl_strdup(value, + &(((sasl_server_conn_t *)conn)->appname), + NULL); + if(result != SASL_OK) MEMERROR(conn); + ((sasl_server_conn_t *)conn)->sparams->appname = + ((sasl_server_conn_t *)conn)->appname; + ((sasl_server_conn_t *)conn)->sparams->applen = + (unsigned) strlen(((sasl_server_conn_t *)conn)->appname); + } else { + ((sasl_server_conn_t *)conn)->sparams->appname = NULL; + ((sasl_server_conn_t *)conn)->sparams->applen = 0; + } + break; + + case SASL_GSS_CREDS: + if(conn->type == SASL_CONN_CLIENT) + ((sasl_client_conn_t *)conn)->cparams->gss_creds = value; + else + ((sasl_server_conn_t *)conn)->sparams->gss_creds = value; + break; + + case SASL_CHANNEL_BINDING: { + const struct sasl_channel_binding *cb = (const struct sasl_channel_binding *)value; + + if (conn->type == SASL_CONN_SERVER) + ((sasl_server_conn_t *)conn)->sparams->cbinding = cb; + else + ((sasl_client_conn_t *)conn)->cparams->cbinding = cb; + break; + } + + case SASL_HTTP_REQUEST: { + const sasl_http_request_t *req = (const sasl_http_request_t *)value; + + if (conn->type == SASL_CONN_SERVER) + ((sasl_server_conn_t *)conn)->sparams->http_request = req; + else + ((sasl_client_conn_t *)conn)->cparams->http_request = req; + break; + } + + default: + sasl_seterror(conn, 0, "Unknown parameter type"); + result = SASL_BADPARAM; + } + + RETURN(conn, result); +} + +/* this is apparently no longer a user function */ +static int sasl_usererr(int saslerr) +{ + /* Hide the difference in a username failure and a password failure */ + if (saslerr == SASL_NOUSER) + return SASL_BADAUTH; + + /* otherwise return the error given; no transform necessary */ + return saslerr; +} + +const char *sasl_errstring(int saslerr, + const char *langlist __attribute__((unused)), + const char **outlang) +{ + if (outlang) *outlang="en-us"; + + switch(saslerr) + { + case SASL_CONTINUE: return "another step is needed in authentication"; + case SASL_OK: return "successful result"; + case SASL_FAIL: return "generic failure"; + case SASL_NOMEM: return "no memory available"; + case SASL_BUFOVER: return "overflowed buffer"; + case SASL_NOMECH: return "no mechanism available"; + case SASL_BADPROT: return "bad protocol / cancel"; + case SASL_NOTDONE: return "can't request information until later in exchange"; + case SASL_BADPARAM: return "invalid parameter supplied"; + case SASL_TRYAGAIN: return "transient failure (e.g., weak key)"; + case SASL_BADMAC: return "integrity check failed"; + case SASL_NOTINIT: return "SASL library is not initialized"; + /* -- client only codes -- */ + case SASL_INTERACT: return "needs user interaction"; + case SASL_BADSERV: return "server failed mutual authentication step"; + case SASL_WRONGMECH: return "mechanism doesn't support requested feature"; + /* -- server only codes -- */ + case SASL_BADAUTH: return "authentication failure"; + case SASL_NOAUTHZ: return "authorization failure"; + case SASL_TOOWEAK: return "mechanism too weak for this user"; + case SASL_ENCRYPT: return "encryption needed to use mechanism"; + case SASL_TRANS: return "One time use of a plaintext password will enable requested mechanism for user"; + case SASL_EXPIRED: return "passphrase expired, has to be reset"; + case SASL_DISABLED: return "account disabled"; + case SASL_NOUSER: return "user not found"; + case SASL_BADVERS: return "version mismatch with plug-in"; + case SASL_UNAVAIL: return "remote authentication server unavailable"; + case SASL_NOVERIFY: return "user exists, but no verifier for user"; + case SASL_PWLOCK: return "passphrase locked"; + case SASL_NOCHANGE: return "requested change was not needed"; + case SASL_WEAKPASS: return "passphrase is too weak for security policy"; + case SASL_NOUSERPASS: return "user supplied passwords are not permitted"; + case SASL_NEED_OLD_PASSWD: return "sasl_setpass needs old password in order " + "to perform password change"; + case SASL_CONSTRAINT_VIOLAT: return "sasl_setpass can't store a property because " + "of a constraint violation"; + case SASL_BADBINDING: return "channel binding failure"; + case SASL_CONFIGERR: return "error when parsing configuration file"; + + default: return "undefined error!"; + } + +} + +/* Return the sanitized error detail about the last error that occured for + * a connection */ +const char *sasl_errdetail(sasl_conn_t *conn) +{ + unsigned need_len; + const char *errstr; + char leader[128]; + + if(!conn) return NULL; + + errstr = sasl_errstring(conn->error_code, NULL, NULL); + snprintf(leader,128,"SASL(%d): %s: ", + sasl_usererr(conn->error_code), errstr); + + need_len = (unsigned) (strlen(leader) + strlen(conn->error_buf) + 12); + if (_buf_alloc(&conn->errdetail_buf, &conn->errdetail_buf_len, need_len) != SASL_OK) { + return NULL; + } + + snprintf(conn->errdetail_buf, need_len, "%s%s", leader, conn->error_buf); + + return conn->errdetail_buf; +} + + +/* Note that this needs the global callbacks, so if you don't give getcallbacks + * a sasl_conn_t, you're going to need to pass it yourself (or else we couldn't + * have client and server at the same time */ +static int _sasl_global_getopt(void *context, + const char *plugin_name, + const char *option, + const char ** result, + unsigned *len) +{ + const sasl_global_callbacks_t * global_callbacks; + const sasl_callback_t *callback; + + global_callbacks = (const sasl_global_callbacks_t *) context; + + if (global_callbacks && global_callbacks->callbacks) { + for (callback = global_callbacks->callbacks; + callback->id != SASL_CB_LIST_END; + callback++) { + if (callback->id == SASL_CB_GETOPT) { + if (!callback->proc) return SASL_FAIL; + if (((sasl_getopt_t *)(callback->proc))(callback->context, + plugin_name, + option, + result, + len) + == SASL_OK) + return SASL_OK; + } + } + } + + /* look it up in our configuration file */ + *result = sasl_config_getstring(option, NULL); + if (*result != NULL) { + if (len) { *len = (unsigned) strlen(*result); } + return SASL_OK; + } + + return SASL_FAIL; +} + +static int +_sasl_conn_getopt(void *context, + const char *plugin_name, + const char *option, + const char ** result, + unsigned *len) +{ + sasl_conn_t * conn; + const sasl_callback_t *callback; + + if (! context) + return SASL_BADPARAM; + + conn = (sasl_conn_t *) context; + + if (conn->callbacks) + for (callback = conn->callbacks; + callback->id != SASL_CB_LIST_END; + callback++) + if (callback->id == SASL_CB_GETOPT + && (((sasl_getopt_t *)(callback->proc))(callback->context, + plugin_name, + option, + result, + len) + == SASL_OK)) + return SASL_OK; + + /* If we made it here, we didn't find an appropriate callback + * in the connection's callback list, or the callback we did + * find didn't return SASL_OK. So we attempt to use the + * global callback for this connection... */ + return _sasl_global_getopt((void *)conn->global_callbacks, + plugin_name, + option, + result, + len); +} + +#ifdef HAVE_SYSLOG +/* this is the default logging */ +static int _sasl_syslog(void *context, + int priority, + const char *message) +{ + int syslog_priority; + sasl_server_conn_t *sconn; + + if (context) { + if (((sasl_conn_t *)context)->type == SASL_CONN_SERVER) { + sconn = (sasl_server_conn_t *)context; + if (sconn->sparams->log_level < priority) + return SASL_OK; + } + } + + /* set syslog priority */ + switch(priority) { + case SASL_LOG_NONE: + return SASL_OK; + break; + case SASL_LOG_ERR: + syslog_priority = LOG_ERR; + break; + case SASL_LOG_WARN: + syslog_priority = LOG_WARNING; + break; + case SASL_LOG_NOTE: + case SASL_LOG_FAIL: + syslog_priority = LOG_NOTICE; + break; + case SASL_LOG_PASS: + case SASL_LOG_TRACE: + case SASL_LOG_DEBUG: + default: + syslog_priority = LOG_DEBUG; + break; + } + + /* do the syslog call. Do not need to call openlog? */ + syslog(syslog_priority | LOG_AUTH, "%s", message); + + return SASL_OK; +} +#endif /* HAVE_SYSLOG */ + +static int +_sasl_getsimple(void *context, + int id, + const char ** result, + size_t *len) +{ + const char *userid; + + if (! context || ! result) return SASL_BADPARAM; + + switch(id) { + case SASL_CB_AUTHNAME: + userid = getenv("USER"); + if (userid != NULL) { + *result = userid; + if (len) *len = strlen(userid); + return SASL_OK; + } + userid = getenv("USERNAME"); + if (userid != NULL) { + *result = userid; + if (len) *len = strlen(userid); + return SASL_OK; + } +#ifdef WIN32 + /* for win32, try using the GetUserName standard call */ + { + DWORD i; + BOOL rval; + static char sender[128]; + + TCHAR tsender[128]; + i = sizeof(tsender) / sizeof(tsender[0]); + rval = GetUserName(tsender, &i); + if ( rval) { /* got a userid */ + WideCharToMultiByte(CP_UTF8, 0, tsender, -1, sender, sizeof(sender), NULL, NULL); /* -1 ensures null-terminated utf8 */ + *result = sender; + if (len) *len = strlen(sender); + return SASL_OK; + } + } +#endif /* WIN32 */ + return SASL_FAIL; + default: + return SASL_BADPARAM; + } +} + +static int +_sasl_getpath(void *context __attribute__((unused)), + const char ** path_dest) +{ +#if !defined(WIN32) + char *path; +#endif + int res = SASL_OK; + + if (! path_dest) { + return SASL_BADPARAM; + } + + /* Only calculate the path once. */ + if (default_plugin_path == NULL) { + +#if defined(WIN32) + /* NB: On Windows platforms this value is always allocated */ + default_plugin_path = _sasl_get_default_win_path(context, + SASL_PLUGIN_PATH_ATTR, + PLUGINDIR); +#else + /* NB: On Unix platforms this value is never allocated */ + path = _sasl_get_default_unix_path(context, + SASL_PATH_ENV_VAR, + PLUGINDIR); + + res = _sasl_strdup(path, &default_plugin_path, NULL); +#endif + } + + if (res == SASL_OK) { + *path_dest = default_plugin_path; + } + + return res; +} + +static int +_sasl_getpath_simple(void *context __attribute__((unused)), + const char **path) +{ + if (! path) { + return SASL_BADPARAM; + } + + if (default_plugin_path == NULL) { + return SASL_FAIL; + } + + *path = default_plugin_path; + + return SASL_OK; +} + +static int +_sasl_getconfpath(void *context __attribute__((unused)), + char ** path_dest) +{ +#if !defined(WIN32) + char *path; +#endif + int res = SASL_OK; + + if (! path_dest) { + return SASL_BADPARAM; + } + + /* Only calculate the path once. */ + if (default_conf_path == NULL) { + +#if defined(WIN32) + /* NB: On Windows platforms this value is always allocated */ + default_conf_path = _sasl_get_default_win_path(context, + SASL_CONF_PATH_ATTR, + CONFIGDIR); +#else + /* NB: On Unix platforms this value is never allocated */ + path = _sasl_get_default_unix_path(context, + SASL_CONF_PATH_ENV_VAR, + CONFIGDIR); + + res = _sasl_strdup(path, &default_conf_path, NULL); +#endif + } + + if (res == SASL_OK) { + *path_dest = default_conf_path; + } + + return res; +} + +static int +_sasl_getconfpath_simple(void *context __attribute__((unused)), + const char **path) +{ + if (! path) { + return SASL_BADPARAM; + } + + if (default_conf_path == NULL) { + return SASL_FAIL; + } + + *path = default_conf_path; + + return SASL_OK; +} + + +static int +_sasl_verifyfile(void *context __attribute__((unused)), + char *file __attribute__((unused)), + int type __attribute__((unused))) +{ + /* always say ok */ + return SASL_OK; +} + + +static int +_sasl_proxy_policy(sasl_conn_t *conn, + void *context __attribute__((unused)), + const char *requested_user, unsigned rlen, + const char *auth_identity, unsigned alen, + const char *def_realm __attribute__((unused)), + unsigned urlen __attribute__((unused)), + struct propctx *propctx __attribute__((unused))) +{ + if (!conn) + return SASL_BADPARAM; + + if (!requested_user || *requested_user == '\0') + return SASL_OK; + + if (!auth_identity || !requested_user || rlen != alen || + (memcmp(auth_identity, requested_user, rlen) != 0)) { + sasl_seterror(conn, 0, + "Requested identity not authenticated identity"); + RETURN(conn, SASL_BADAUTH); + } + + return SASL_OK; +} + +int _sasl_getcallback(sasl_conn_t * conn, + unsigned long callbackid, + sasl_callback_ft *pproc, + void **pcontext) +{ + const sasl_callback_t *callback; + + if (!pproc || !pcontext) + PARAMERROR(conn); + + /* Some callbacks are always provided by the library */ + switch (callbackid) { + case SASL_CB_LIST_END: + /* Nothing ever gets to provide this */ + INTERROR(conn, SASL_FAIL); + case SASL_CB_GETOPT: + if (conn) { + *pproc = (sasl_callback_ft)&_sasl_conn_getopt; + *pcontext = conn; + } else { + *pproc = (sasl_callback_ft)&_sasl_global_getopt; + *pcontext = NULL; + } + return SASL_OK; + } + + /* If it's not always provided by the library, see if there's + * a version provided by the application for this connection... */ + if (conn && conn->callbacks) { + for (callback = conn->callbacks; callback->id != SASL_CB_LIST_END; + callback++) { + if (callback->id == callbackid) { + *pproc = callback->proc; + *pcontext = callback->context; + if (callback->proc) { + return SASL_OK; + } else { + return SASL_INTERACT; + } + } + } + } + + /* And, if not for this connection, see if there's one + * for all {server,client} connections... */ + if (conn && conn->global_callbacks && conn->global_callbacks->callbacks) { + for (callback = conn->global_callbacks->callbacks; + callback->id != SASL_CB_LIST_END; + callback++) { + if (callback->id == callbackid) { + *pproc = callback->proc; + *pcontext = callback->context; + if (callback->proc) { + return SASL_OK; + } else { + return SASL_INTERACT; + } + } + } + } + + /* Otherwise, see if the library provides a default callback. */ + switch (callbackid) { +#ifdef HAVE_SYSLOG + case SASL_CB_LOG: + *pproc = (sasl_callback_ft)&_sasl_syslog; + *pcontext = conn; + return SASL_OK; +#endif /* HAVE_SYSLOG */ + case SASL_CB_GETPATH: + *pproc = default_getpath_cb.proc; + *pcontext = default_getpath_cb.context; + return SASL_OK; + case SASL_CB_GETCONFPATH: + *pproc = default_getconfpath_cb.proc; + *pcontext = default_getconfpath_cb.context; + return SASL_OK; + case SASL_CB_AUTHNAME: + *pproc = (sasl_callback_ft)&_sasl_getsimple; + *pcontext = conn; + return SASL_OK; + case SASL_CB_VERIFYFILE: + *pproc = (sasl_callback_ft)&_sasl_verifyfile; + *pcontext = NULL; + return SASL_OK; + case SASL_CB_PROXY_POLICY: + *pproc = (sasl_callback_ft)&_sasl_proxy_policy; + *pcontext = NULL; + return SASL_OK; + } + + /* Unable to find a callback... */ + *pproc = NULL; + *pcontext = NULL; + sasl_seterror(conn, SASL_NOLOG, "Unable to find a callback: %d", callbackid); + RETURN(conn,SASL_FAIL); +} + + +/* + * This function is typically called from a plugin. + * It creates a string from the formatting and varargs given + * and calls the logging callback (syslog by default) + * + * %m will parse the value in the next argument as an errno string + * %z will parse the next argument as a SASL error code. + */ + +void +_sasl_log (sasl_conn_t *conn, + int level, + const char *fmt, + ...) +{ + char *out = NULL; + size_t alloclen=100; /* current allocated length */ + size_t outlen=0; /* current length of output buffer */ + size_t formatlen; + size_t pos=0; /* current position in format string */ + int result; + sasl_log_t *log_cb; + void *log_ctx; + + int ival; + unsigned int uval; + char *cval; + va_list ap; /* varargs thing */ + + if(!fmt) return; + + out = (char *) sasl_ALLOC(250); + if(!out) return; + + formatlen = strlen(fmt); + + /* See if we have a logging callback... */ + result = _sasl_getcallback(conn, SASL_CB_LOG, (sasl_callback_ft *)&log_cb, &log_ctx); + if (result == SASL_OK && ! log_cb) + result = SASL_FAIL; + if (result != SASL_OK) goto done; + + va_start(ap, fmt); /* start varargs */ + + while(pos<formatlen) + { + if (fmt[pos]!='%') /* regular character */ + { + result = _buf_alloc(&out, &alloclen, outlen+1); + if (result != SASL_OK) goto done; + out[outlen]=fmt[pos]; + outlen++; + pos++; + + } else { /* formating thing */ + int done=0; + char frmt[10]; + int frmtpos=1; + char tempbuf[21]; + frmt[0]='%'; + pos++; + + while (done==0) + { + switch(fmt[pos]) + { + case 's': /* need to handle this */ + cval = va_arg(ap, char *); /* get the next arg */ + result = _sasl_add_string(&out, &alloclen, + &outlen, cval); + + if (result != SASL_OK) /* add the string */ + goto done; + + done=1; + break; + + case '%': /* double % output the '%' character */ + result = _buf_alloc(&out,&alloclen,outlen+1); + if (result != SASL_OK) + goto done; + + out[outlen]='%'; + outlen++; + done=1; + break; + + case 'm': /* insert the errno string */ + result = _sasl_add_string(&out, &alloclen, &outlen, + strerror(va_arg(ap, int))); + if (result != SASL_OK) + goto done; + + done=1; + break; + + case 'z': /* insert the sasl error string */ + result = _sasl_add_string(&out, &alloclen, &outlen, + (char *) sasl_errstring(va_arg(ap, int),NULL,NULL)); + if (result != SASL_OK) + goto done; + + done=1; + break; + + case 'c': + frmt[frmtpos++]=fmt[pos]; + frmt[frmtpos]=0; + tempbuf[0] = (char) va_arg(ap, int); /* get the next arg */ + tempbuf[1]='\0'; + + /* now add the character */ + result = _sasl_add_string(&out, &alloclen, &outlen, tempbuf); + if (result != SASL_OK) + goto done; + + done=1; + break; + + case 'd': + case 'i': + frmt[frmtpos++]=fmt[pos]; + frmt[frmtpos]=0; + ival = va_arg(ap, int); /* get the next arg */ + + snprintf(tempbuf,20,frmt,ival); /* have snprintf do the work */ + /* now add the string */ + result = _sasl_add_string(&out, &alloclen, &outlen, tempbuf); + if (result != SASL_OK) + goto done; + + done=1; + break; + + case 'o': + case 'u': + case 'x': + case 'X': + frmt[frmtpos++]=fmt[pos]; + frmt[frmtpos]=0; + uval = va_arg(ap, unsigned int); /* get the next arg */ + + snprintf(tempbuf,20,frmt,uval); /* have snprintf do the work */ + /* now add the string */ + result = _sasl_add_string(&out, &alloclen, &outlen, tempbuf); + if (result != SASL_OK) + goto done; + + done=1; + break; + + default: + frmt[frmtpos++]=fmt[pos]; /* add to the formating */ + frmt[frmtpos]=0; + if (frmtpos>9) + done=1; + } + pos++; + if (pos>formatlen) + done=1; + } + + } + } + + /* put 0 at end */ + result = _buf_alloc(&out, &alloclen, outlen+1); + if (result != SASL_OK) goto done; + out[outlen]=0; + + /* send log message */ + result = log_cb(log_ctx, level, out); + + done: + va_end(ap); + if(out) sasl_FREE(out); +} + + + +/* Allocate and Init a sasl_utils_t structure */ +sasl_utils_t * +_sasl_alloc_utils(sasl_conn_t *conn, + sasl_global_callbacks_t *global_callbacks) +{ + sasl_utils_t *utils; + /* set util functions - need to do rest*/ + utils=sasl_ALLOC(sizeof(sasl_utils_t)); + if (utils==NULL) + return NULL; + + utils->conn = conn; + + sasl_randcreate(&utils->rpool); + + if (conn) { + utils->getopt = &_sasl_conn_getopt; + utils->getopt_context = conn; + } else { + utils->getopt = &_sasl_global_getopt; + utils->getopt_context = global_callbacks; + } + + utils->malloc=_sasl_allocation_utils.malloc; + utils->calloc=_sasl_allocation_utils.calloc; + utils->realloc=_sasl_allocation_utils.realloc; + utils->free=_sasl_allocation_utils.free; + + utils->mutex_alloc = _sasl_mutex_utils.alloc; + utils->mutex_lock = _sasl_mutex_utils.lock; + utils->mutex_unlock = _sasl_mutex_utils.unlock; + utils->mutex_free = _sasl_mutex_utils.free; + + utils->MD5Init = &_sasl_MD5Init; + utils->MD5Update= &_sasl_MD5Update; + utils->MD5Final = &_sasl_MD5Final; + utils->hmac_md5 = &_sasl_hmac_md5; + utils->hmac_md5_init = &_sasl_hmac_md5_init; + utils->hmac_md5_final = &_sasl_hmac_md5_final; + utils->hmac_md5_precalc = &_sasl_hmac_md5_precalc; + utils->hmac_md5_import = &_sasl_hmac_md5_import; + utils->mkchal = &sasl_mkchal; + utils->utf8verify = &sasl_utf8verify; + utils->rand=&sasl_rand; + utils->churn=&sasl_churn; + utils->checkpass=NULL; + + utils->encode64=&sasl_encode64; + utils->decode64=&sasl_decode64; + + utils->erasebuffer=&sasl_erasebuffer; + + utils->getprop=&sasl_getprop; + utils->setprop=&sasl_setprop; + + utils->getcallback=&_sasl_getcallback; + + utils->log=&_sasl_log; + + utils->seterror=&sasl_seterror; + +#ifndef macintosh + /* Aux Property Utilities */ + utils->prop_new=&prop_new; + utils->prop_dup=&prop_dup; + utils->prop_request=&prop_request; + utils->prop_get=&prop_get; + utils->prop_getnames=&prop_getnames; + utils->prop_clear=&prop_clear; + utils->prop_dispose=&prop_dispose; + utils->prop_format=&prop_format; + utils->prop_set=&prop_set; + utils->prop_setvals=&prop_setvals; + utils->prop_erase=&prop_erase; + utils->auxprop_store=&sasl_auxprop_store; +#endif + + /* Spares */ + utils->spare_fptr = NULL; + utils->spare_fptr1 = utils->spare_fptr2 = NULL; + + return utils; +} + +int +_sasl_free_utils(const sasl_utils_t ** utils) +{ + sasl_utils_t *nonconst; + + if(!utils) return SASL_BADPARAM; + if(!*utils) return SASL_OK; + + /* I wish we could avoid this cast, it's pretty gratuitous but it + * does make life easier to have it const everywhere else. */ + nonconst = (sasl_utils_t *)(*utils); + + sasl_randfree(&(nonconst->rpool)); + sasl_FREE(nonconst); + + *utils = NULL; + return SASL_OK; +} + +int sasl_idle(sasl_conn_t *conn) +{ + if (! conn) { + if (_sasl_server_idle_hook + && _sasl_server_idle_hook(NULL)) + return 1; + if (_sasl_client_idle_hook + && _sasl_client_idle_hook(NULL)) + return 1; + return 0; + } + + if (conn->idle_hook) + return conn->idle_hook(conn); + + return 0; +} + +static const sasl_callback_t * +_sasl_find_callback_by_type (const sasl_callback_t *callbacks, + unsigned long id) +{ + if (callbacks) { + while (callbacks->id != SASL_CB_LIST_END) { + if (callbacks->id == id) { + return callbacks; + } else { + ++callbacks; + } + } + } + return NULL; +} + +const sasl_callback_t * +_sasl_find_getpath_callback(const sasl_callback_t *callbacks) +{ + callbacks = _sasl_find_callback_by_type (callbacks, SASL_CB_GETPATH); + if (callbacks != NULL) { + return callbacks; + } else { + return &default_getpath_cb; + } +} + +const sasl_callback_t * +_sasl_find_getconfpath_callback(const sasl_callback_t *callbacks) +{ + callbacks = _sasl_find_callback_by_type (callbacks, SASL_CB_GETCONFPATH); + if (callbacks != NULL) { + return callbacks; + } else { + return &default_getconfpath_cb; + } +} + +const sasl_callback_t * +_sasl_find_verifyfile_callback(const sasl_callback_t *callbacks) +{ + static const sasl_callback_t default_verifyfile_cb = { + SASL_CB_VERIFYFILE, + (sasl_callback_ft)&_sasl_verifyfile, + NULL + }; + + callbacks = _sasl_find_callback_by_type (callbacks, SASL_CB_VERIFYFILE); + if (callbacks != NULL) { + return callbacks; + } else { + return &default_verifyfile_cb; + } +} + +/* Basically a conditional call to realloc(), if we need more */ +int _buf_alloc(char **rwbuf, size_t *curlen, size_t newlen) +{ + if(!(*rwbuf)) { + *rwbuf = sasl_ALLOC((unsigned)newlen); + if (*rwbuf == NULL) { + *curlen = 0; + return SASL_NOMEM; + } + *curlen = newlen; + } else if(*rwbuf && *curlen < newlen) { + size_t needed = 2*(*curlen); + + while(needed < newlen) + needed *= 2; + + /* WARN - We will leak the old buffer on failure */ + *rwbuf = sasl_REALLOC(*rwbuf, (unsigned)needed); + + if (*rwbuf == NULL) { + *curlen = 0; + return SASL_NOMEM; + } + *curlen = needed; + } + + return SASL_OK; +} + +/* for the mac os x cfm glue: this lets the calling function + get pointers to the error buffer without having to touch the sasl_conn_t struct */ +void _sasl_get_errorbuf(sasl_conn_t *conn, char ***bufhdl, size_t **lenhdl) +{ + *bufhdl = &conn->error_buf; + *lenhdl = &conn->error_buf_len; +} + +/* convert an iovec to a single buffer */ +int _iovec_to_buf(const struct iovec *vec, + unsigned numiov, buffer_info_t **output) +{ + unsigned i; + int ret; + buffer_info_t *out; + char *pos; + + if (!vec || !output) return SASL_BADPARAM; + + if (!(*output)) { + *output = sasl_ALLOC(sizeof(buffer_info_t)); + if (!*output) return SASL_NOMEM; + memset(*output,0,sizeof(buffer_info_t)); + } + + out = *output; + + out->curlen = 0; + for (i = 0; i < numiov; i++) { + out->curlen += vec[i].iov_len; + } + + ret = _buf_alloc(&out->data, &out->reallen, out->curlen); + + if (ret != SASL_OK) return SASL_NOMEM; + + memset(out->data, 0, out->reallen); + pos = out->data; + + for (i = 0; i < numiov; i++) { + memcpy(pos, vec[i].iov_base, vec[i].iov_len); + pos += vec[i].iov_len; + } + + return SASL_OK; +} + +/* This code might be useful in the future, but it isn't now, so.... */ +#if 0 +int _sasl_iptostring(const struct sockaddr *addr, socklen_t addrlen, + char *out, unsigned outlen) { + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; + int niflags; + + if(!addr || !out) return SASL_BADPARAM; + + niflags = (NI_NUMERICHOST | NI_NUMERICSERV); +#ifdef NI_WITHSCOPEID + if (addr->sa_family == AF_INET6) + niflags |= NI_WITHSCOPEID; +#endif + if (getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), + niflags) != 0) + return SASL_BADPARAM; + + if(outlen < strlen(hbuf) + strlen(pbuf) + 2) + return SASL_BUFOVER; + + snprintf(out, outlen, "%s;%s", hbuf, pbuf); + + return SASL_OK; +} +#endif + +int _sasl_ipfromstring(const char *addr, + struct sockaddr *out, socklen_t outlen) +{ + int i, j; + struct addrinfo hints, *ai = NULL; + char hbuf[NI_MAXHOST]; + + /* A NULL out pointer just implies we don't do a copy, just verify it */ + + if(!addr) return SASL_BADPARAM; + + /* Parse the address */ + for (i = 0; addr[i] != '\0' && addr[i] != ';'; i++) { + if (i >= NI_MAXHOST) + return SASL_BADPARAM; + hbuf[i] = addr[i]; + } + hbuf[i] = '\0'; + + if (addr[i] == ';') + i++; + /* XXX: Do we need this check? */ + for (j = i; addr[j] != '\0'; j++) + if (!isdigit((int)(addr[j]))) + return SASL_BADPARAM; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0) + return SASL_BADPARAM; + + if (out) { + if (outlen < (socklen_t)ai->ai_addrlen) { + freeaddrinfo(ai); + return SASL_BUFOVER; + } + memcpy(out, ai->ai_addr, ai->ai_addrlen); + } + + freeaddrinfo(ai); + + return SASL_OK; +} + +int _sasl_build_mechlist(void) +{ + int count = 0; + sasl_string_list_t *clist = NULL, *slist = NULL, *olist = NULL; + sasl_string_list_t *p, *q, **last, *p_next; + + clist = _sasl_client_mechs(); + slist = _sasl_server_mechs(); + + if(!clist) { + olist = slist; + } else { + int flag; + + /* append slist to clist, and set olist to clist */ + for(p = slist; p; p = p_next) { + flag = 0; + p_next = p->next; + + last = &clist; + for(q = clist; q; q = q->next) { + if(!strcmp(q->d, p->d)) { + /* They match, set the flag */ + flag = 1; + break; + } + last = &(q->next); + } + + if(!flag) { + *last = p; + p->next = NULL; + } else { + sasl_FREE(p); + } + } + + olist = clist; + } + + if(!olist) { + /* This is not going to be very useful */ + printf ("no olist"); + return SASL_FAIL; + } + + for (p = olist; p; p = p->next) count++; + + if(global_mech_list) { + sasl_FREE(global_mech_list); + global_mech_list = NULL; + } + + global_mech_list = sasl_ALLOC((count + 1) * sizeof(char *)); + if(!global_mech_list) return SASL_NOMEM; + + memset(global_mech_list, 0, (count + 1) * sizeof(char *)); + + count = 0; + for (p = olist; p; p = p_next) { + p_next = p->next; + + global_mech_list[count++] = (char *) p->d; + + sasl_FREE(p); + } + + return SASL_OK; +} + +const char ** sasl_global_listmech(void) +{ + return (const char **)global_mech_list; +} + +int sasl_listmech(sasl_conn_t *conn, + const char *user, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount) +{ + if(!conn) { + return SASL_BADPARAM; + } else if(conn->type == SASL_CONN_SERVER) { + RETURN(conn, _sasl_server_listmech(conn, user, prefix, sep, suffix, + result, plen, pcount)); + } else if (conn->type == SASL_CONN_CLIENT) { + RETURN(conn, _sasl_client_listmech(conn, prefix, sep, suffix, + result, plen, pcount)); + } + + PARAMERROR(conn); +} + +int _sasl_is_equal_mech(const char *req_mech, + const char *plug_mech, + size_t req_mech_len, + int *plus) +{ + size_t n; + + if (req_mech_len > 5 && + strcasecmp(&req_mech[req_mech_len - 5], "-PLUS") == 0) { + n = req_mech_len - 5; + *plus = 1; + } else { + n = req_mech_len; + *plus = 0; + } + + if (n < strlen(plug_mech)) { + /* Don't allow arbitrary prefix match */ + return 0; + } + + return (strncasecmp(req_mech, plug_mech, n) == 0); +} + +#ifndef WIN32 +static char * +_sasl_get_default_unix_path(void *context __attribute__((unused)), + char * env_var_name, + char * default_value) +{ + char *path = NULL; + + /* Honor external variable only in a safe environment */ + if (getuid() == geteuid() && getgid() == getegid()) { + path = getenv(env_var_name); + } + if (! path) { + path = default_value; + } + + return path; +} + +#else /*WIN32*/ +/* Return NULL on failure */ +static char * +_sasl_get_default_win_path(void *context __attribute__((unused)), + TCHAR * reg_attr_name, + char * default_value) +{ + /* Open registry entry, and find all registered SASL libraries. + * + * Registry location: + * + * SOFTWARE\\Carnegie Mellon\\Project Cyrus\\SASL Library + * + * Key - value: + * + * "SearchPath" - value: PATH like (';' delimited) list + * of directories where to search for plugins + * The list may contain references to environment + * variables (e.g. %PATH%). + * + */ + HKEY hKey; + DWORD ret; + DWORD ValueType; /* value type */ + DWORD cbData; /* value size in bytes and later number of wchars */ + TCHAR * ValueData; /* value */ + DWORD cbExpandedData; /* "expanded" value size in wchars */ + TCHAR * ExpandedValueData; /* "expanded" value */ + TCHAR * return_value; /* function return value */ + TCHAR * tmp; + + /* Initialization */ + ExpandedValueData = NULL; + ValueData = NULL; + return_value = NULL; + + /* Open the registry */ + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + SASL_ROOT_KEY, + 0, + KEY_READ, + &hKey); + + if (ret != ERROR_SUCCESS) { + /* no registry entry */ + char *ret; + (void) _sasl_strdup (default_value, &ret, NULL); + return ret; + } + + /* figure out value type and required buffer size */ + /* the size will include space for terminating NUL if required */ + RegQueryValueEx (hKey, + reg_attr_name, + NULL, /* reserved */ + &ValueType, + NULL, + &cbData); + + /* Only accept string related types */ + if (ValueType != REG_EXPAND_SZ && + ValueType != REG_MULTI_SZ && + ValueType != REG_SZ) { + return_value = NULL; + goto CLEANUP; + } + + /* Any high water mark? */ + ValueData = sasl_ALLOC(cbData + 2 * sizeof(TCHAR)); /* extra bytes to insert null-terminator if it's missed */ + if (ValueData == NULL) { + return_value = NULL; + goto CLEANUP; + }; + + if (RegQueryValueEx(hKey, + reg_attr_name, + NULL, /* reserved */ + &ValueType, + (LPBYTE)ValueData, + &cbData) != ERROR_SUCCESS) { + return_value = NULL; + goto CLEANUP; + } + cbData /= sizeof(TCHAR); /* covert to number of symbols */ + ValueData[cbData] = '\0'; /* MS docs say we have to to that */ + ValueData[cbData + 1] = '\0'; /* for MULTI */ + + switch (ValueType) { + case REG_EXPAND_SZ: + /* : A random starting guess */ + cbExpandedData = cbData + 1024; + ExpandedValueData = (TCHAR*)sasl_ALLOC(cbExpandedData * sizeof(TCHAR)); + if (ExpandedValueData == NULL) { + return_value = NULL; + goto CLEANUP; + }; + + + cbExpandedData = ExpandEnvironmentStrings( + ValueData, + ExpandedValueData, + cbExpandedData); + + if (cbExpandedData == 0) { + /* : GetLastError() contains the reason for failure */ + return_value = NULL; + goto CLEANUP; + } + + /* : Must retry expansion with the bigger buffer */ + if (cbExpandedData > cbData + 1024) { + /* : Memory leak here if can't realloc */ + ExpandedValueData = sasl_REALLOC(ExpandedValueData, cbExpandedData * sizeof(TCHAR)); + if (ExpandedValueData == NULL) { + return_value = NULL; + goto CLEANUP; + }; + + cbExpandedData = ExpandEnvironmentStrings( + ValueData, + ExpandedValueData, + cbExpandedData); + + /* : This should not happen */ + if (cbExpandedData == 0) { + /* : GetLastError() contains the reason for failure */ + return_value = NULL; + goto CLEANUP; + } + } + + sasl_FREE(ValueData); + ValueData = ExpandedValueData; + /* : This is to prevent automatical freeing of this block on cleanup */ + ExpandedValueData = NULL; + + break; + + case REG_MULTI_SZ: + tmp = ValueData; + + /* : We shouldn't overflow here, as the buffer is guarantied + : to contain at least two consequent NULs */ + while (1) { + if (tmp[0] == '\0') { + /* : Stop the process if we found the end of the string (two consequent NULs) */ + if (tmp[1] == '\0') { + break; + } + + /* : Replace delimiting NUL with our delimiter characted */ + tmp[0] = PATHS_DELIMITER; + } + tmp += (_tcslen(tmp)); + } + break; + + case REG_SZ: + /* Do nothing, it is good as is */ + break; + + default: + return_value = NULL; + goto CLEANUP; + } + + return_value = ValueData; /* just to flag we have a result */ + +CLEANUP: + RegCloseKey(hKey); + if (ExpandedValueData != NULL) sasl_FREE(ExpandedValueData); + if (return_value == NULL) { + if (ValueData != NULL) sasl_FREE(ValueData); + return NULL; + } + if (sizeof(TCHAR) == sizeof(char)) { + return (char*)return_value; + } + + /* convert to utf-8 for compatibility with other OS' */ + { + char *tmp = _sasl_wchar_to_utf8(return_value); + sasl_FREE(return_value); + return tmp; + } +} + +char* _sasl_wchar_to_utf8(WCHAR *str) +{ + size_t bufLen = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + char *buf = sasl_ALLOC(bufLen); + if (buf) { + if (WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, bufLen, NULL, NULL) == 0) { /* -1 ensures null-terminated utf8 */ + sasl_FREE(buf); + buf = NULL; + } + } + return buf; +} + +WCHAR* _sasl_utf8_to_wchar(const char *str) +{ + size_t bufLen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + WCHAR *buf = sasl_ALLOC(bufLen * sizeof(WCHAR)); + if (buf) { + if (MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, bufLen) == 0) { /* -1 ensures null-terminated utf8 */ + sasl_FREE(buf); + buf = NULL; + } + } + return buf; +} + +#endif /*WIN32*/ diff --git a/contrib/libs/sasl/lib/config.c b/contrib/libs/sasl/lib/config.c new file mode 100644 index 0000000000..d1a5b973de --- /dev/null +++ b/contrib/libs/sasl/lib/config.c @@ -0,0 +1,168 @@ +/* SASL Config file API + * Rob Siemborski + * Tim Martin (originally in Cyrus distribution) + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +#include "sasl.h" +#include "saslint.h" + +struct configlist { + char *key; + char *value; +}; + +static struct configlist *configlist = NULL; +static int nconfiglist = 0; + +#define CONFIGLISTGROWSIZE 100 + +int sasl_config_init(const char *filename) +{ + FILE *infile; + int lineno = 0; + int alloced = 0; + char buf[4096]; + char *p, *key; + char *tail; + int result; + + nconfiglist=0; + + infile = fopen(filename, "r"); + if (!infile) { + return SASL_CONTINUE; + } + + while (fgets(buf, sizeof(buf), infile)) { + lineno++; + + if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; + for (p = buf; *p && isspace((int) *p); p++); + if (!*p || *p == '#') continue; + + key = p; + while (*p && (isalnum((int) *p) || *p == '-' || *p == '_')) { + if (isupper((int) *p)) *p = (char) tolower(*p); + p++; + } + if (*p != ':') { + fclose(infile); + return SASL_CONFIGERR; + } + *p++ = '\0'; + + while (*p && isspace((int) *p)) p++; + + if (!*p) { + fclose(infile); + return SASL_CONFIGERR; + } + + /* Now strip trailing spaces, if any */ + tail = p + strlen(p) - 1; + while (tail > p && isspace((int) *tail)) { + *tail = '\0'; + tail--; + } + + if (nconfiglist == alloced) { + alloced += CONFIGLISTGROWSIZE; + configlist=sasl_REALLOC((char *)configlist, + alloced * sizeof(struct configlist)); + if (configlist == NULL) { + fclose(infile); + return SASL_NOMEM; + } + } + + result = _sasl_strdup(key, + &(configlist[nconfiglist].key), + NULL); + if (result != SASL_OK) { + fclose(infile); + return result; + } + result = _sasl_strdup(p, + &(configlist[nconfiglist].value), + NULL); + if (result != SASL_OK) { + fclose(infile); + return result; + } + + nconfiglist++; + } + fclose(infile); + + return SASL_OK; +} + +const char *sasl_config_getstring(const char *key,const char *def) +{ + int opt; + + for (opt = 0; opt < nconfiglist; opt++) { + if (*key == configlist[opt].key[0] && + !strcmp(key, configlist[opt].key)) + return configlist[opt].value; + } + return def; +} + +void sasl_config_done(void) +{ + int opt; + + for (opt = 0; opt < nconfiglist; opt++) { + if (configlist[opt].key) sasl_FREE(configlist[opt].key); + if (configlist[opt].value) sasl_FREE(configlist[opt].value); + } + + sasl_FREE(configlist); + configlist = NULL; + nconfiglist = 0; +} diff --git a/contrib/libs/sasl/lib/dlopen.c b/contrib/libs/sasl/lib/dlopen.c new file mode 100644 index 0000000000..8284cd8700 --- /dev/null +++ b/contrib/libs/sasl/lib/dlopen.c @@ -0,0 +1,569 @@ +/* dlopen.c--Unix dlopen() dynamic loader interface + * Rob Siemborski + * Rob Earhart + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdlib.h> +#include <errno.h> +#include <stdio.h> +#include <limits.h> + +#include <sasl.h> +#include "saslint.h" + +#ifndef PIC +#include <saslplug.h> +#include "staticopen.h" +#endif + +#ifdef DO_DLOPEN +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else /* HAVE_DIRENT_H */ +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif /* ! HAVE_DIRENT_H */ + +#ifndef NAME_MAX +# ifdef _POSIX_NAME_MAX +# define NAME_MAX _POSIX_NAME_MAX +# else +# define NAME_MAX 16 +# endif +#endif + +#if NAME_MAX < 8 +# define NAME_MAX 8 +#endif + +#ifdef __hpux +#ifndef HAVE_DLFCN_H +#include <dl.h> + +typedef shl_t * dll_handle; +typedef void * dll_func; + +dll_handle +dlopen(char *fname, int mode) +{ + shl_t h = shl_load(fname, BIND_DEFERRED, 0L); + shl_t *hp = NULL; + + if (h) { + hp = (shl_t *)malloc(sizeof (shl_t)); + if (!hp) { + shl_unload(h); + } else { + *hp = h; + } + } + + return (dll_handle)hp; +} + +int +dlclose(dll_handle hp) +{ + shl_t h; + + if (hp != NULL) { + h = *((shl_t *)hp); + free(hp); + return shl_unload(h); + } else { + /* Return error */ + return -1; + } +} + +dll_func +dlsym(dll_handle h, char *n) +{ + dll_func handle; + + if (shl_findsym ((shl_t *)h, n, TYPE_PROCEDURE, &handle)) + return NULL; + + return (dll_func)handle; +} + +char *dlerror() +{ + if (errno != 0) { + return strerror(errno); + } + return "Generic shared library error"; +} + +#endif /* HAVE_DLFCN_H */ + +#ifdef __ia64 +#define SO_SUFFIX ".so" +#else +#define SO_SUFFIX ".sl" +#endif /* __ia64 */ + +#else /* __hpux */ +#define SO_SUFFIX ".so" +#endif + +#define LA_SUFFIX ".la" + +typedef struct lib_list +{ + struct lib_list *next; + void *library; +} lib_list_t; + +static lib_list_t *lib_list_head = NULL; + +#endif /* DO_DLOPEN */ + +int _sasl_locate_entry(void *library, const char *entryname, + void **entry_point) +{ +#ifdef DO_DLOPEN +/* note that we still check for known problem systems in + * case we are cross-compiling */ +#if defined(DLSYM_NEEDS_UNDERSCORE) || (defined(__OpenBSD__) && !defined(__ELF__)) + char adj_entryname[1024]; +#else +#define adj_entryname entryname +#endif + + if(!entryname) { + _sasl_log(NULL, SASL_LOG_ERR, + "no entryname in _sasl_locate_entry"); + return SASL_BADPARAM; + } + + if(!library) { + _sasl_log(NULL, SASL_LOG_ERR, + "no library in _sasl_locate_entry"); + return SASL_BADPARAM; + } + + if(!entry_point) { + _sasl_log(NULL, SASL_LOG_ERR, + "no entrypoint output pointer in _sasl_locate_entry"); + return SASL_BADPARAM; + } + +#if defined(DLSYM_NEEDS_UNDERSCORE) || (defined(__OpenBSD__) && !defined(__ELF__)) + snprintf(adj_entryname, sizeof adj_entryname, "_%s", entryname); +#endif + + *entry_point = NULL; + *entry_point = dlsym(library, adj_entryname); + if (*entry_point == NULL) { +#if 0 /* This message appears to confuse people */ + _sasl_log(NULL, SASL_LOG_DEBUG, + "unable to get entry point %s: %s", adj_entryname, + dlerror()); +#endif + return SASL_FAIL; + } + + return SASL_OK; +#else + return SASL_FAIL; +#endif /* DO_DLOPEN */ +} + +#ifdef DO_DLOPEN + +static int _sasl_plugin_load(char *plugin, void *library, + const char *entryname, + int (*add_plugin)(const char *, void *)) +{ + void *entry_point; + int result; + + result = _sasl_locate_entry(library, entryname, &entry_point); + if(result == SASL_OK) { + result = add_plugin(plugin, entry_point); + if(result != SASL_OK) + _sasl_log(NULL, SASL_LOG_DEBUG, + "_sasl_plugin_load failed on %s for plugin: %s\n", + entryname, plugin); + } + + return result; +} + +/* this returns the file to actually open. + * out should be a buffer of size PATH_MAX + * and may be the same as in. */ + +/* We'll use a static buffer for speed unless someone complains */ +#define MAX_LINE 2048 + +static int _parse_la(const char *prefix, const char *in, char *out) +{ + FILE *file; + size_t length; + char line[MAX_LINE]; + char *ntmp = NULL; + + if(!in || !out || !prefix || out == in) return SASL_BADPARAM; + + /* Set this so we can detect failure */ + *out = '\0'; + + length = strlen(in); + + if (strcmp(in + (length - strlen(LA_SUFFIX)), LA_SUFFIX)) { + if(!strcmp(in + (length - strlen(SO_SUFFIX)),SO_SUFFIX)) { + /* check for a .la file */ + if (strlen(prefix) + strlen(in) + strlen(LA_SUFFIX) + 1 >= MAX_LINE) + return SASL_BADPARAM; + strcpy(line, prefix); + strcat(line, in); + length = strlen(line); + *(line + (length - strlen(SO_SUFFIX))) = '\0'; + strcat(line, LA_SUFFIX); + file = fopen(line, "r"); + if(file) { + /* We'll get it on the .la open */ + fclose(file); + return SASL_FAIL; + } + } + if (strlen(prefix) + strlen(in) + 1 >= PATH_MAX) + return SASL_BADPARAM; + strcpy(out, prefix); + strcat(out, in); + return SASL_OK; + } + + if (strlen(prefix) + strlen(in) + 1 >= MAX_LINE) + return SASL_BADPARAM; + strcpy(line, prefix); + strcat(line, in); + + file = fopen(line, "r"); + if(!file) { + _sasl_log(NULL, SASL_LOG_WARN, + "unable to open LA file: %s", line); + return SASL_FAIL; + } + + while(!feof(file)) { + if(!fgets(line, MAX_LINE, file)) break; + if(line[strlen(line) - 1] != '\n') { + _sasl_log(NULL, SASL_LOG_WARN, + "LA file has too long of a line: %s", in); + fclose(file); + return SASL_BUFOVER; + } + if(line[0] == '\n' || line[0] == '#') continue; + if(!strncmp(line, "dlname=", sizeof("dlname=") - 1)) { + /* We found the line with the name in it */ + char *end; + char *start; + size_t len; + end = strrchr(line, '\''); + if(!end) continue; + start = &line[sizeof("dlname=")-1]; + len = strlen(start); + if(len > 3 && start[0] == '\'') { + ntmp=&start[1]; + *end='\0'; + /* Do we have dlname="" ? */ + if(ntmp == end) { + _sasl_log(NULL, SASL_LOG_DEBUG, + "dlname is empty in .la file: %s", in); + fclose(file); + return SASL_FAIL; + } + strcpy(out, prefix); + strcat(out, ntmp); + } + break; + } + } + if(ferror(file) || feof(file)) { + _sasl_log(NULL, SASL_LOG_WARN, + "Error reading .la: %s\n", in); + fclose(file); + return SASL_FAIL; + } + fclose(file); + + if(!(*out)) { + _sasl_log(NULL, SASL_LOG_WARN, + "Could not find a dlname line in .la file: %s", in); + return SASL_FAIL; + } + + return SASL_OK; +} +#endif /* DO_DLOPEN */ + +/* loads a plugin library */ +int _sasl_get_plugin(const char *file, + const sasl_callback_t *verifyfile_cb, + void **libraryptr) +{ +#ifdef DO_DLOPEN + int r = 0; + int flag; + void *library; + lib_list_t *newhead; + + r = ((sasl_verifyfile_t *)(verifyfile_cb->proc)) + (verifyfile_cb->context, file, SASL_VRFY_PLUGIN); + if (r != SASL_OK) return r; + +#ifdef RTLD_NOW + flag = RTLD_NOW; +#else + flag = 0; +#endif + + newhead = sasl_ALLOC(sizeof(lib_list_t)); + if(!newhead) return SASL_NOMEM; + + if (!(library = dlopen(file, flag))) { + _sasl_log(NULL, SASL_LOG_ERR, + "unable to dlopen %s: %s", file, dlerror()); + sasl_FREE(newhead); + return SASL_FAIL; + } + + newhead->library = library; + newhead->next = lib_list_head; + lib_list_head = newhead; + + *libraryptr = library; + return SASL_OK; +#else + return SASL_FAIL; +#endif /* DO_DLOPEN */ +} + +/* gets the list of mechanisms */ +int _sasl_load_plugins(const add_plugin_list_t *entrypoints, + const sasl_callback_t *getpath_cb, + const sasl_callback_t *verifyfile_cb) +{ + int result; + const add_plugin_list_t *cur_ep; +#ifdef DO_DLOPEN + char str[PATH_MAX], tmp[PATH_MAX+2], prefix[PATH_MAX+2]; + /* 1 for '/' 1 for trailing '\0' */ + char c; + int pos; + const char *path=NULL; + int position; + DIR *dp; + struct dirent *dir; +#endif +#ifndef PIC + add_plugin_t *add_plugin; + _sasl_plug_type type; + _sasl_plug_rec *p; +#endif + + if (! entrypoints + || ! getpath_cb + || getpath_cb->id != SASL_CB_GETPATH + || ! getpath_cb->proc + || ! verifyfile_cb + || verifyfile_cb->id != SASL_CB_VERIFYFILE + || ! verifyfile_cb->proc) + return SASL_BADPARAM; + +#ifndef PIC + /* do all the static plugins first */ + + for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) { + + /* What type of plugin are we looking for? */ + if(!strcmp(cur_ep->entryname, "sasl_server_plug_init")) { + type = SERVER; + add_plugin = (add_plugin_t *)sasl_server_add_plugin; + } else if (!strcmp(cur_ep->entryname, "sasl_client_plug_init")) { + type = CLIENT; + add_plugin = (add_plugin_t *)sasl_client_add_plugin; + } else if (!strcmp(cur_ep->entryname, "sasl_auxprop_plug_init")) { + type = AUXPROP; + add_plugin = (add_plugin_t *)sasl_auxprop_add_plugin; + } else if (!strcmp(cur_ep->entryname, "sasl_canonuser_init")) { + type = CANONUSER; + add_plugin = (add_plugin_t *)sasl_canonuser_add_plugin; + } else { + /* What are we looking for then? */ + return SASL_FAIL; + } + for (p=_sasl_static_plugins; p->type; p++) { + if(type == p->type) + result = add_plugin(p->name, p->plug); + } + } +#endif /* !PIC */ + +/* only do the following if: + * + * we support dlopen() + * AND we are not staticly compiled + * OR we are staticly compiled and TRY_DLOPEN_WHEN_STATIC is defined + */ +#if defined(DO_DLOPEN) && (defined(PIC) || (!defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) + /* get the path to the plugins */ + result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context, + &path); + if (result != SASL_OK) return result; + if (! path) return SASL_FAIL; + + if (strlen(path) >= PATH_MAX) { /* no you can't buffer overrun */ + return SASL_FAIL; + } + + position=0; + do { + pos=0; + do { + c=path[position]; + position++; + str[pos]=c; + pos++; + } while ((c!=':') && (c!='=') && (c!=0)); + str[pos-1]='\0'; + + strcpy(prefix,str); + strcat(prefix,"/"); + + if ((dp=opendir(str)) !=NULL) /* ignore errors */ + { + while ((dir=readdir(dp)) != NULL) + { + size_t length; + void *library; + char *c; + char plugname[PATH_MAX]; + char name[PATH_MAX]; + + length = NAMLEN(dir); + if (length < 4) + continue; /* can not possibly be what we're looking for */ + + if (length + pos>=PATH_MAX) continue; /* too big */ + + if (strcmp(dir->d_name + (length - strlen(SO_SUFFIX)), + SO_SUFFIX) + && strcmp(dir->d_name + (length - strlen(LA_SUFFIX)), + LA_SUFFIX)) + continue; + + memcpy(name,dir->d_name,length); + name[length]='\0'; + + result = _parse_la(prefix, name, tmp); + if(result != SASL_OK) + continue; + + /* skip "lib" and cut off suffix -- + this only need be approximate */ + strcpy(plugname, name + 3); + c = strchr(plugname, (int)'.'); + if(c) *c = '\0'; + + result = _sasl_get_plugin(tmp, verifyfile_cb, &library); + + if(result != SASL_OK) + continue; + + for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) { + _sasl_plugin_load(plugname, library, cur_ep->entryname, + cur_ep->add_plugin); + /* If this fails, it's not the end of the world */ + } + } + + closedir(dp); + } else { + _sasl_log(NULL, SASL_LOG_DEBUG, + "looking for plugins in '%s', failed to open directory, error: %s", + str, + strerror(errno)); + } + + } while ((c!='=') && (c!=0)); +#endif /* defined(DO_DLOPEN) && (!defined(PIC) || (defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) */ + + return SASL_OK; +} + +int +_sasl_done_with_plugins(void) +{ +#ifdef DO_DLOPEN + lib_list_t *libptr, *libptr_next; + + for(libptr = lib_list_head; libptr; libptr = libptr_next) { + libptr_next = libptr->next; + if(libptr->library) + dlclose(libptr->library); + sasl_FREE(libptr); + } + + lib_list_head = NULL; +#endif /* DO_DLOPEN */ + return SASL_OK; +} diff --git a/contrib/libs/sasl/lib/external.c b/contrib/libs/sasl/lib/external.c new file mode 100644 index 0000000000..4650838bf2 --- /dev/null +++ b/contrib/libs/sasl/lib/external.c @@ -0,0 +1,407 @@ +/* SASL server API implementation + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <ctype.h> +#include <string.h> +#include <sasl.h> +#include <saslplug.h> +#include "saslint.h" + +#include "../common/plugin_common.h" + +/***************************** Common Section *****************************/ + +/***************************** Server Section *****************************/ + +static int +external_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + if (!conn_context + || !sparams + || !sparams->utils + || !sparams->utils->conn) + return SASL_BADPARAM; + + if (!sparams->utils->conn->external.auth_id) + return SASL_NOMECH; + + *conn_context = NULL; + + return SASL_OK; +} + +static int +external_server_mech_step(void *conn_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + int result; + + if (!sparams + || !sparams->utils + || !sparams->utils->conn + || !sparams->utils->getcallback + || !serverout + || !serveroutlen + || !oparams) + return SASL_BADPARAM; + + if (!sparams->utils->conn->external.auth_id) + return SASL_BADPROT; + + /* xxx arbitrary limit here */ + if (clientinlen > 16384) return SASL_BADPROT; + + if ((sparams->props.security_flags & SASL_SEC_NOANONYMOUS) && + (!strcmp(sparams->utils->conn->external.auth_id, "anonymous"))) { + sasl_seterror(sparams->utils->conn,0,"anonymous login not allowed"); + return SASL_NOAUTHZ; + } + + *serverout = NULL; + *serveroutlen = 0; + + if (!clientin) { + /* No initial data; we're in a protocol which doesn't support it. + * So we let the server app know that we need some... */ + return SASL_CONTINUE; + } + + if (clientinlen) { /* if we have a non-zero authorization id */ + /* The user's trying to authorize as someone they didn't + * authenticate as */ + result = sparams->canon_user(sparams->utils->conn, + clientin, clientinlen, + SASL_CU_AUTHZID, oparams); + if(result != SASL_OK) return result; + + result = sparams->canon_user(sparams->utils->conn, + sparams->utils->conn->external.auth_id, 0, + SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VERIFIED, oparams); + } else { + result = sparams->canon_user(sparams->utils->conn, + sparams->utils->conn->external.auth_id, 0, + SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VERIFIED | SASL_CU_AUTHZID, oparams); + } + + if (result != SASL_OK) return result; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static int +external_server_mech_avail(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + void **conn_context __attribute__((unused))) +{ + if (!sparams->utils->conn->external.auth_id) { + /* Return Temporary Failure */ + return SASL_NOTDONE; + } + + return SASL_OK; +} + +static sasl_server_plug_t external_server_plugins[] = +{ + { + "EXTERNAL", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NODICTIONARY, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* glob_context */ + &external_server_mech_new, /* mech_new */ + &external_server_mech_step, /* mech_step */ + NULL, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + &external_server_mech_avail, /* mech_avail */ + NULL /* spare */ + } +}; + +int external_server_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (!out_version || !pluglist || !plugcount) + return SASL_BADPARAM; + + if (max_version != SASL_SERVER_PLUG_VERSION) { + SETERROR( utils, "EXTERNAL version mismatch" ); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = external_server_plugins; + *plugcount = 1; + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context +{ + char *out_buf; + size_t out_buf_len; +} client_context_t; + +static int external_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + client_context_t *text; + + if (!params + || !params->utils + || !params->utils->conn + || !conn_context) + return SASL_BADPARAM; + + if (!params->utils->conn->external.auth_id) + return SASL_NOMECH; + + text = sasl_ALLOC(sizeof(client_context_t)); + if(!text) return SASL_NOMEM; + + memset(text, 0, sizeof(client_context_t)); + + *conn_context = text; + + return SASL_OK; +} + +static int +external_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin __attribute__((unused)), + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + client_context_t *text = (client_context_t *)conn_context; + const char *user = NULL; + int user_result = SASL_OK; + int result; + + if (!params + || !params->utils + || !params->utils->conn + || !params->utils->getcallback + || !clientout + || !clientoutlen + || !oparams) + return SASL_BADPARAM; + + if (!params->utils->conn->external.auth_id) + return SASL_BADPROT; + + if (serverinlen != 0) + return SASL_BADPROT; + + *clientout = NULL; + *clientoutlen = 0; + + /* try to get the userid */ + if (user == NULL) { + user_result = _plug_get_userid(params->utils, &user, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) + return user_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if (user_result == SASL_INTERACT) { + /* make the prompt list */ + int result = + _plug_make_prompts(params->utils, prompt_need, + "Please enter your authorization name", + "", + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (result != SASL_OK) return result; + + return SASL_INTERACT; + } + + *clientoutlen = user ? (unsigned) strlen(user) : 0; + + result = _buf_alloc(&text->out_buf, &text->out_buf_len, *clientoutlen + 1); + + if (result != SASL_OK) return result; + + if (user && *user) { + result = params->canon_user(params->utils->conn, + user, 0, SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + + result = params->canon_user(params->utils->conn, + params->utils->conn->external.auth_id, 0, + SASL_CU_AUTHID, oparams); + if (result != SASL_OK) return result; + + memcpy(text->out_buf, user, *clientoutlen); + } else { + result = params->canon_user(params->utils->conn, + params->utils->conn->external.auth_id, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + } + + text->out_buf[*clientoutlen] = '\0'; + + *clientout = text->out_buf; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static void +external_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils __attribute__((unused))) +{ + client_context_t *text = (client_context_t *) conn_context; + + if (!text) return; + + if(text->out_buf) sasl_FREE(text->out_buf); + + sasl_FREE(text); +} + +static const unsigned long external_required_prompts[] = { + SASL_CB_LIST_END +}; + +static sasl_client_plug_t external_client_plugins[] = +{ + { + "EXTERNAL", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NODICTIONARY, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + external_required_prompts, /* required_prompts */ + NULL, /* glob_context */ + &external_client_mech_new, /* mech_new */ + &external_client_mech_step, /* mech_step */ + &external_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int external_client_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (!utils || !out_version || !pluglist || !plugcount) + return SASL_BADPARAM; + + if (max_version != SASL_CLIENT_PLUG_VERSION) { + SETERROR( utils, "EXTERNAL version mismatch" ); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = external_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/lib/md5.c b/contrib/libs/sasl/lib/md5.c new file mode 100644 index 0000000000..c79f4f7d77 --- /dev/null +++ b/contrib/libs/sasl/lib/md5.c @@ -0,0 +1,527 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Function names changed to avoid namespace collisions: Rob Siemborski */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +#include <config.h> +#include "md5global.h" +#include "md5.h" +#include "hmac-md5.h" + +#ifndef WIN32 +# include <arpa/inet.h> +#endif + +/* Constants for MD5Transform routine. +*/ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((SASL_UINT4 [4], const unsigned char [64])); +static void Encode PROTO_LIST + ((unsigned char *, SASL_UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((SASL_UINT4 *, const unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + + */ +#ifdef I +/* This might be defined via NANA */ +#undef I +#endif + +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + + */ + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. +*/ + +#define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (SASL_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (SASL_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (SASL_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (SASL_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. +*/ + +void _sasl_MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the context. +*/ + +void _sasl_MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +const unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((SASL_UINT4)inputLen << 3)) + < ((SASL_UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((SASL_UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); MD5Transform + (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); + +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. +*/ + +void _sasl_MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + _sasl_MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + _sasl_MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ + +static void MD5Transform (state, block) +SASL_UINT4 state[4]; +const unsigned char block[64]; +{ + SASL_UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (SASL_UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + + */ + +static void Encode (output, input, len) +unsigned char *output; +SASL_UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (SASL_UINT4). Assumes len is + a multiple of 4. + + */ + +static void Decode (output, input, len) +SASL_UINT4 *output; +const unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((SASL_UINT4)input[j]) | (((SASL_UINT4)input[j+1]) << 8) | (((SASL_UINT4)input[j+2]) << 16) + | (((SASL_UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. +*/ + +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} + +void _sasl_hmac_md5_init(HMAC_MD5_CTX *hmac, + const unsigned char *key, + int key_len) +{ + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + _sasl_MD5Init(&tctx); + _sasl_MD5Update(&tctx, key, key_len); + _sasl_MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + MD5_memset((POINTER)k_ipad, '\0', sizeof k_ipad); + MD5_memset((POINTER)k_opad, '\0', sizeof k_opad); + MD5_memcpy( k_ipad, (POINTER)key, key_len); + MD5_memcpy( k_opad, (POINTER)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + _sasl_MD5Init(&hmac->ictx); /* init inner context */ + _sasl_MD5Update(&hmac->ictx, k_ipad, 64); /* apply inner pad */ + + _sasl_MD5Init(&hmac->octx); /* init outer context */ + _sasl_MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */ + + /* scrub the pads and key context (if used) */ + MD5_memset((POINTER)&k_ipad, 0, sizeof(k_ipad)); + MD5_memset((POINTER)&k_opad, 0, sizeof(k_opad)); + MD5_memset((POINTER)&tk, 0, sizeof(tk)); + + /* and we're done. */ +} + +/* The precalc and import routines here rely on the fact that we pad + * the key out to 64 bytes and use that to initialize the md5 + * contexts, and that updating an md5 context with 64 bytes of data + * leaves nothing left over; all of the interesting state is contained + * in the state field, and none of it is left over in the count and + * buffer fields. So all we have to do is save the state field; we + * can zero the others when we reload it. Which is why the decision + * was made to pad the key out to 64 bytes in the first place. */ +void _sasl_hmac_md5_precalc(HMAC_MD5_STATE *state, + const unsigned char *key, + int key_len) +{ + HMAC_MD5_CTX hmac; + unsigned lupe; + + _sasl_hmac_md5_init(&hmac, key, key_len); + for (lupe = 0; lupe < 4; lupe++) { + state->istate[lupe] = htonl(hmac.ictx.state[lupe]); + state->ostate[lupe] = htonl(hmac.octx.state[lupe]); + } + MD5_memset((POINTER)&hmac, 0, sizeof(hmac)); +} + + +void _sasl_hmac_md5_import(HMAC_MD5_CTX *hmac, + HMAC_MD5_STATE *state) +{ + unsigned lupe; + MD5_memset((POINTER)hmac, 0, sizeof(HMAC_MD5_CTX)); + for (lupe = 0; lupe < 4; lupe++) { + hmac->ictx.state[lupe] = ntohl(state->istate[lupe]); + hmac->octx.state[lupe] = ntohl(state->ostate[lupe]); + } + /* Init the counts to account for our having applied + * 64 bytes of key; this works out to 0x200 (64 << 3; see + * MD5Update above...) */ + hmac->ictx.count[0] = hmac->octx.count[0] = 0x200; +} + +void _sasl_hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], + HMAC_MD5_CTX *hmac) +{ + _sasl_MD5Final(digest, &hmac->ictx); /* Finalize inner md5 */ + _sasl_MD5Update(&hmac->octx, digest, 16); /* Update outer ctx */ + _sasl_MD5Final(digest, &hmac->octx); /* Finalize outer md5 */ +} + + +void _sasl_hmac_md5(text, text_len, key, key_len, digest) +const unsigned char* text; /* pointer to data stream */ +int text_len; /* length of data stream */ +const unsigned char* key; /* pointer to authentication key */ +int key_len; /* length of authentication key */ +unsigned char *digest; /* caller digest to be filled in */ +{ + MD5_CTX context; + + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + _sasl_MD5Init(&tctx); + _sasl_MD5Update(&tctx, key, key_len); + _sasl_MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + MD5_memset(k_ipad, '\0', sizeof k_ipad); + MD5_memset(k_opad, '\0', sizeof k_opad); + MD5_memcpy( k_ipad, (POINTER)key, key_len); + MD5_memcpy( k_opad, (POINTER)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + + _sasl_MD5Init(&context); /* init context for 1st + * pass */ + _sasl_MD5Update(&context, k_ipad, 64); /* start with inner pad */ + _sasl_MD5Update(&context, text, text_len); /* then text of datagram */ + _sasl_MD5Final(digest, &context); /* finish up 1st pass */ + + /* + * perform outer MD5 + */ + _sasl_MD5Init(&context); /* init context for 2nd + * pass */ + _sasl_MD5Update(&context, k_opad, 64); /* start with outer pad */ + _sasl_MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + _sasl_MD5Final(digest, &context); /* finish up 2nd pass */ + +} diff --git a/contrib/libs/sasl/lib/saslint.h b/contrib/libs/sasl/lib/saslint.h new file mode 100644 index 0000000000..ebade78f3a --- /dev/null +++ b/contrib/libs/sasl/lib/saslint.h @@ -0,0 +1,544 @@ +/* saslint.h - internal SASL library definitions + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SASLINT_H +#define SASLINT_H + +#include <config.h> +#include "sasl.h" +#include "saslplug.h" +#include "saslutil.h" +#include "prop.h" + +#ifndef INLINE +#if defined (WIN32) +/* Visual Studio: "inline" keyword is not available in C, only in C++ */ +#define INLINE __inline +#else +#define INLINE inline +#endif +#endif + +/* #define'd constants */ +#define CANON_BUF_SIZE 1024 + +/* Error Handling Foo */ +/* Helpful Hints: + * -Error strings are set as soon as possible (first function in stack trace + * with a pointer to the sasl_conn_t. + * -Error codes are set as late as possible (only in the sasl api functions), + * though "as often as possible" also comes to mind to ensure correctness + * -Errors from calls to _buf_alloc, _sasl_strdup, etc are assumed to be + * memory errors. + * -Only errors (error codes < SASL_OK) should be remembered + */ +#define RETURN(conn, val) { if(conn && (val) < SASL_OK) \ + (conn)->error_code = (val); \ + return (val); } +#define MEMERROR(conn) {\ + if(conn) sasl_seterror( (conn), 0, \ + "Out of Memory in " __FILE__ " near line %d", __LINE__ ); \ + RETURN(conn, SASL_NOMEM) } +#define PARAMERROR(conn) {\ + if(conn) sasl_seterror( (conn), SASL_NOLOG, \ + "Parameter error in " __FILE__ " near line %d", __LINE__ ); \ + RETURN(conn, SASL_BADPARAM) } +#define INTERROR(conn, val) {\ + if(conn) sasl_seterror( (conn), 0, \ + "Internal Error %d in " __FILE__ " near line %d", (val),\ + __LINE__ ); \ + RETURN(conn, (val)) } + +#ifndef PATH_MAX +# ifdef WIN32 +# define PATH_MAX MAX_PATH +# else +# ifdef _POSIX_PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# else +# define PATH_MAX 1024 /* arbitrary; probably big enough. + * will probably only be 256+64 on + * pre-posix machines */ +# endif /* _POSIX_PATH_MAX */ +# endif /* WIN32 */ +#endif + +/* : Define directory delimiter in SASL_PATH/SASL_CONF_PATH variables */ +#ifdef WIN32 +#define PATHS_DELIMITER ';' +#else +#define PATHS_DELIMITER ':' +#endif + +/* A FQDN max len is 255 per RFC 1035, + * this means 253 chars max, we add one more for zero terminator */ +#define MAXFQDNLEN 254 + +/* Datatype Definitions */ +typedef struct { + const sasl_callback_t *callbacks; + const char *appname; +} sasl_global_callbacks_t; + +typedef struct _sasl_external_properties +{ + sasl_ssf_t ssf; + char *auth_id; +} _sasl_external_properties_t; + +typedef struct sasl_string_list +{ + const char *d; + struct sasl_string_list *next; +} sasl_string_list_t; + +typedef struct buffer_info +{ + char *data; + size_t curlen; + size_t reallen; +} buffer_info_t; + +typedef int add_plugin_t(const char *, void *); + +typedef struct add_plugin_list +{ + const char *entryname; + add_plugin_t *add_plugin; +} add_plugin_list_t; + +enum Sasl_conn_type { SASL_CONN_UNKNOWN = 0, + SASL_CONN_SERVER = 1, + SASL_CONN_CLIENT = 2 }; + +struct sasl_conn { + enum Sasl_conn_type type; + + void (*destroy_conn)(sasl_conn_t *); /* destroy function */ + + char *service; + + unsigned int flags; /* flags passed to sasl_*_new */ + + /* IP information. A buffer of size 52 is adequate for this in its + longest format (see sasl.h) */ + int got_ip_local, got_ip_remote; + char iplocalport[NI_MAXHOST + NI_MAXSERV]; + char ipremoteport[NI_MAXHOST + NI_MAXSERV]; + + void *context; + sasl_out_params_t oparams; + + sasl_security_properties_t props; + _sasl_external_properties_t external; + + sasl_secret_t *secret; + + int (*idle_hook)(sasl_conn_t *conn); + const sasl_callback_t *callbacks; + const sasl_global_callbacks_t *global_callbacks; /* global callbacks + * connection */ + char *serverFQDN; + + /* Pointers to memory that we are responsible for */ + buffer_info_t *encode_buf; + + int error_code; + char *error_buf, *errdetail_buf; + size_t error_buf_len, errdetail_buf_len; + char *mechlist_buf; + size_t mechlist_buf_len; + + char *decode_buf; + + char user_buf[CANON_BUF_SIZE+1], authid_buf[CANON_BUF_SIZE+1]; + + /* Allocated by sasl_encodev if the output contains multiple SASL packet. */ + buffer_info_t multipacket_encoded_data; +}; + +/* Server Conn Type Information */ + +typedef struct mechanism +{ + server_sasl_mechanism_t m; + struct mechanism *next; +} mechanism_t; + +typedef struct mech_list { + const sasl_utils_t *utils; /* gotten from plug_init */ + + void *mutex; /* mutex for this data */ + mechanism_t *mech_list; /* list of loaded mechanisms */ + int mech_length; /* number of loaded mechanisms */ +} mech_list_t; + +typedef struct context_list +{ + mechanism_t *mech; + void *context; /* if NULL, this mech is disabled for this connection + * otherwise, use this context instead of a call + * to mech_new */ + struct context_list *next; +} context_list_t; + +typedef struct sasl_server_conn { + sasl_conn_t base; /* parts common to server + client */ + + char *appname; /* application name buffer (for sparams) */ + char *user_realm; /* domain the user authenticating is in */ + int sent_last; /* Have we already done the last send? */ + int authenticated; + mechanism_t *mech; /* mechanism trying to use */ + sasl_server_params_t *sparams; + context_list_t *mech_contexts; + mechanism_t *mech_list; /* list of available mechanisms */ + int mech_length; /* number of available mechanisms */ +} sasl_server_conn_t; + +/* Client Conn Type Information */ + +typedef struct cmechanism +{ + client_sasl_mechanism_t m; + struct cmechanism *next; +} cmechanism_t; + +typedef struct cmech_list { + const sasl_utils_t *utils; + + void *mutex; /* mutex for this data */ + cmechanism_t *mech_list; /* list of mechanisms */ + int mech_length; /* number of mechanisms */ + +} cmech_list_t; + +typedef struct sasl_client_conn { + sasl_conn_t base; /* parts common to server + client */ + + cmechanism_t *mech; + sasl_client_params_t *cparams; + + char *clientFQDN; + + cmechanism_t *mech_list; /* list of available mechanisms */ + int mech_length; /* number of available mechanisms */ +} sasl_client_conn_t; + +typedef struct sasl_allocation_utils { + sasl_malloc_t *malloc; + sasl_calloc_t *calloc; + sasl_realloc_t *realloc; + sasl_free_t *free; +} sasl_allocation_utils_t; + +typedef struct sasl_mutex_utils { + sasl_mutex_alloc_t *alloc; + sasl_mutex_lock_t *lock; + sasl_mutex_unlock_t *unlock; + sasl_mutex_free_t *free; +} sasl_mutex_utils_t; + +typedef struct sasl_log_utils_s { + sasl_log_t *log; +} sasl_log_utils_t; + +typedef int sasl_plaintext_verifier(sasl_conn_t *conn, + const char *userid, + const char *passwd, + const char *service, + const char *user_realm); + +struct sasl_verify_password_s { + char *name; + sasl_plaintext_verifier *verify; +}; + +/* + * globals & constants + */ +/* + * common.c + */ +LIBSASL_API const sasl_utils_t *sasl_global_utils; + +extern int (*_sasl_client_idle_hook)(sasl_conn_t *conn); +extern int (*_sasl_server_idle_hook)(sasl_conn_t *conn); + +/* These return SASL_OK if we've actually finished cleanup, + * SASL_NOTINIT if that part of the library isn't initialized, and + * SASL_CONTINUE if we need to call them again */ +extern int (*_sasl_client_cleanup_hook)(void); +extern int (*_sasl_server_cleanup_hook)(void); + +extern sasl_allocation_utils_t _sasl_allocation_utils; +extern sasl_mutex_utils_t _sasl_mutex_utils; +extern int _sasl_allocation_locked; + +void sasl_common_done(void); + +extern int _sasl_is_equal_mech(const char *req_mech, + const char *plug_mech, + size_t req_mech_len, + int *plus); + +/* + * checkpw.c + */ +extern struct sasl_verify_password_s _sasl_verify_password[]; + +/* + * server.c + */ +/* (this is a function call to ensure this is read-only to the outside) */ +extern int _is_sasl_server_active(void); + +/* + * Allocation and Mutex utility macros + */ +#define sasl_ALLOC(__size__) (_sasl_allocation_utils.malloc((__size__))) +#define sasl_CALLOC(__nelem__, __size__) \ + (_sasl_allocation_utils.calloc((__nelem__), (__size__))) +#define sasl_REALLOC(__ptr__, __size__) \ + (_sasl_allocation_utils.realloc((__ptr__), (__size__))) +#define sasl_FREE(__ptr__) (_sasl_allocation_utils.free((__ptr__))) + +#define sasl_MUTEX_ALLOC() (_sasl_mutex_utils.alloc()) +#define sasl_MUTEX_LOCK(__mutex__) (_sasl_mutex_utils.lock((__mutex__))) +#define sasl_MUTEX_UNLOCK(__mutex__) (_sasl_mutex_utils.unlock((__mutex__))) +#define sasl_MUTEX_FREE(__mutex__) \ + (_sasl_mutex_utils.free((__mutex__))) + +/* function prototypes */ +/* + * dlopen.c and staticopen.c + */ +/* + * The differences here are: + * _sasl_load_plugins loads all plugins from all files + * _sasl_get_plugin loads the LIBRARY for an individual file + * _sasl_done_with_plugins frees the LIBRARIES loaded by the above 2 + * _sasl_locate_entry locates an entrypoint in a given library + */ +extern int _sasl_load_plugins(const add_plugin_list_t *entrypoints, + const sasl_callback_t *getpath_callback, + const sasl_callback_t *verifyfile_callback); +extern int _sasl_get_plugin(const char *file, + const sasl_callback_t *verifyfile_cb, + void **libraryptr); +extern int _sasl_locate_entry(void *library, const char *entryname, + void **entry_point); +extern int _sasl_done_with_plugins(); + +/* + * common.c + */ +extern const sasl_callback_t * +_sasl_find_getpath_callback(const sasl_callback_t *callbacks); + +extern const sasl_callback_t * +_sasl_find_getconfpath_callback(const sasl_callback_t *callbacks); + +extern const sasl_callback_t * +_sasl_find_verifyfile_callback(const sasl_callback_t *callbacks); + +extern int _sasl_common_init(sasl_global_callbacks_t *global_callbacks); + +extern int _sasl_conn_init(sasl_conn_t *conn, + const char *service, + unsigned int flags, + enum Sasl_conn_type type, + int (*idle_hook)(sasl_conn_t *conn), + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *callbacks, + const sasl_global_callbacks_t *global_callbacks); +extern void _sasl_conn_dispose(sasl_conn_t *conn); + +extern sasl_utils_t * +_sasl_alloc_utils(sasl_conn_t *conn, + sasl_global_callbacks_t *global_callbacks); +extern int _sasl_free_utils(const sasl_utils_t ** utils); + +extern int +_sasl_getcallback(sasl_conn_t * conn, + unsigned long callbackid, + sasl_callback_ft * pproc, + void **pcontext); + +extern void +_sasl_log(sasl_conn_t *conn, + int level, + const char *fmt, + ...); + +void _sasl_get_errorbuf(sasl_conn_t *conn, char ***bufhdl, size_t **lenhdl); +int _sasl_add_string(char **out, size_t *alloclen, + size_t *outlen, const char *add); + +/* More Generic Utilities in common.c */ +extern int _sasl_strdup(const char *in, char **out, size_t *outlen); + +/* Basically a conditional call to realloc(), if we need more */ +int _buf_alloc(char **rwbuf, size_t *curlen, size_t newlen); + +/* convert an iovec to a single buffer */ +int _iovec_to_buf(const struct iovec *vec, + unsigned numiov, buffer_info_t **output); + +/* Convert between string formats and sockaddr formats */ +int _sasl_iptostring(const struct sockaddr *addr, socklen_t addrlen, + char *out, unsigned outlen); +int _sasl_ipfromstring(const char *addr, struct sockaddr *out, + socklen_t outlen); + +/* + * external plugin (external.c) + */ +int external_client_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount); +int external_server_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount); + +/* Mech Listing Functions */ +int _sasl_build_mechlist(void); +int _sasl_server_listmech(sasl_conn_t *conn, + const char *user, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount); +int _sasl_client_listmech(sasl_conn_t *conn, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount); +/* Just create a straight list of them */ +sasl_string_list_t *_sasl_client_mechs(void); +sasl_string_list_t *_sasl_server_mechs(void); + +/* + * config file declarations (config.c) + */ +extern const char *sasl_config_getstring(const char *key,const char *def); + +/* checkpw.c */ +#ifdef DO_SASL_CHECKAPOP +extern int _sasl_auxprop_verify_apop(sasl_conn_t *conn, + const char *userstr, + const char *challenge, + const char *response, + const char *user_realm); +#endif /* DO_SASL_CHECKAPOP */ + +/* Auxprop Plugin (sasldb.c) */ +extern int sasldb_auxprop_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_auxprop_plug_t **plug, + const char *plugname); + +/* + * auxprop.c + */ +extern int _sasl_auxprop_add_plugin(void *p, void *library); +extern void _sasl_auxprop_free(void); +extern int _sasl_auxprop_lookup(sasl_server_params_t *sparams, + unsigned flags, + const char *user, unsigned ulen); + +/* + * canonusr.c + */ +void _sasl_canonuser_free(); +extern int internal_canonuser_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_canonuser_plug_t **plug, + const char *plugname); +extern int _sasl_canon_user(sasl_conn_t *conn, + const char *user, + unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams); +int _sasl_canon_user_lookup (sasl_conn_t *conn, + const char *user, + unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams); + +/* + * saslutil.c + */ +int get_fqhostname( + char *name, + int namelen, + int abort_if_no_fqdn + ); + +#ifndef HAVE_GETHOSTNAME +#ifdef sun +/* gotta define gethostname ourselves on suns */ +extern int gethostname(char *, int); +#endif +#endif /* HAVE_GETHOSTNAME */ + +#ifdef WIN32 +char* _sasl_wchar_to_utf8(WCHAR *str); +WCHAR* _sasl_utf8_to_wchar(const char *str); +#endif + +#endif /* SASLINT_H */ diff --git a/contrib/libs/sasl/lib/saslutil.c b/contrib/libs/sasl/lib/saslutil.c new file mode 100644 index 0000000000..46c628c7f5 --- /dev/null +++ b/contrib/libs/sasl/lib/saslutil.c @@ -0,0 +1,790 @@ +/* saslutil.c + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if defined(WIN32) +#define _CRT_RAND_S +#endif + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#include "saslint.h" +#include <saslutil.h> + +/* Contains: + * + * sasl_decode64 + * sasl_encode64 + * sasl_mkchal + * sasl_utf8verify + * sasl_randcreate + * sasl_randfree + * sasl_randseed + * sasl_rand + * sasl_churn + * sasl_erasebuffer + */ + +char *encode_table; +char *decode_table; + +#define RPOOL_SIZE 3 +struct sasl_rand_s { + unsigned short pool[RPOOL_SIZE]; + /* since the init time might be really bad let's make this lazy */ + int initialized; +}; + +#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) + +static char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????"; + +static signed char index_64[128] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +}; + +/* base64 encode + * in -- input data + * inlen -- input data length + * out -- output buffer (will be NUL terminated) + * outmax -- max size of output buffer + * result: + * outlen -- gets actual length of output buffer (optional) + * + * Returns SASL_OK on success, SASL_BUFOVER if result won't fit + */ + +int sasl_encode64(const char *_in, + unsigned inlen, + char *_out, + unsigned outmax, + unsigned *outlen) +{ + const unsigned char *in = (const unsigned char *)_in; + unsigned char *out = (unsigned char *)_out; + unsigned char oval; + unsigned olen; + + /* check params */ + if ((inlen > 0) && (in == NULL)) return SASL_BADPARAM; + + /* Will it fit? */ + olen = (inlen + 2) / 3 * 4; + if (outlen) { + *outlen = olen; + } + if (outmax <= olen) { + return SASL_BUFOVER; + } + + /* Do the work... */ + while (inlen >= 3) { + /* user provided max buffer size; make sure we don't go over it */ + *out++ = basis_64[in[0] >> 2]; + *out++ = basis_64[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *out++ = basis_64[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *out++ = basis_64[in[2] & 0x3f]; + in += 3; + inlen -= 3; + } + if (inlen > 0) { + /* user provided max buffer size; make sure we don't go over it */ + *out++ = basis_64[in[0] >> 2]; + oval = (in[0] << 4) & 0x30; + if (inlen > 1) oval |= in[1] >> 4; + *out++ = basis_64[oval]; + *out++ = (inlen < 2) ? '=' : basis_64[(in[1] << 2) & 0x3c]; + *out++ = '='; + } + + *out = '\0'; + + return SASL_OK; +} + +/* base64 decode + * in -- input data + * inlen -- length of input data + * out -- output data (may be same as in, must have enough space) + * outmax -- max size of output buffer + * result: + * outlen -- actual output length + * + * returns: + * SASL_BADPROT on bad base64, + * SASL_BUFOVER if result won't fit, + * SASL_CONTINUE on a partial block, + * SASL_OK on success + */ + +int sasl_decode64(const char *in, + unsigned inlen, + char *out, + unsigned outmax, /* size of the buffer, not counting the NUL */ + unsigned *outlen) +{ + unsigned len = 0; + unsigned j; + int c[4]; + int saw_equal = 0; + + /* check parameters */ + if (out == NULL) return SASL_FAIL; + + if (inlen > 0 && *in == '\r') return SASL_FAIL; + + while (inlen > 3) { + /* No data is valid after an '=' character */ + if (saw_equal) { + return SASL_BADPROT; + } + + for (j = 0; j < 4; j++) { + c[j] = in[0]; + in++; + inlen--; + } + + if (CHAR64(c[0]) == -1 || CHAR64(c[1]) == -1) return SASL_BADPROT; + if (c[2] != '=' && CHAR64(c[2]) == -1) return SASL_BADPROT; + if (c[3] != '=' && CHAR64(c[3]) == -1) return SASL_BADPROT; + /* No data is valid after a '=' character, unless it is another '=' */ + if (c[2] == '=' && c[3] != '=') return SASL_BADPROT; + if (c[2] == '=' || c[3] == '=') { + saw_equal = 1; + } + + *out++ = (CHAR64(c[0]) << 2) | (CHAR64(c[1]) >> 4); + if (++len >= outmax) return SASL_BUFOVER; + if (c[2] != '=') { + *out++ = ((CHAR64(c[1]) << 4) & 0xf0) | (CHAR64(c[2]) >> 2); + if (++len >= outmax) return SASL_BUFOVER; + if (c[3] != '=') { + *out++ = ((CHAR64(c[2]) << 6) & 0xc0) | CHAR64(c[3]); + if (++len >= outmax) return SASL_BUFOVER; + } + } + } + + *out = '\0'; /* NUL terminate the output string */ + + if (outlen) *outlen = len; + + if (inlen != 0) { + if (saw_equal) { + /* Unless there is CRLF at the end? */ + return SASL_BADPROT; + } else { + return (SASL_CONTINUE); + } + } + + return SASL_OK; +} + +/* make a challenge string (NUL terminated) + * buf -- buffer for result + * maxlen -- max length of result + * hostflag -- 0 = don't include hostname, 1 = include hostname + * returns final length or 0 if not enough space + */ + +int sasl_mkchal(sasl_conn_t *conn, + char *buf, + unsigned maxlen, + unsigned hostflag) +{ + sasl_rand_t *pool = NULL; + unsigned long randnum; + int ret; + time_t now; + unsigned len; + + len = 4 /* <.>\0 */ + + (2 * 20); /* 2 numbers, 20 => max size of 64bit + * ulong in base 10 */ + if (hostflag && conn->serverFQDN) + len += (unsigned) strlen(conn->serverFQDN) + 1 /* for the @ */; + + if (maxlen < len) + return 0; + + ret = sasl_randcreate(&pool); + if(ret != SASL_OK) return 0; /* xxx sasl return code? */ + + sasl_rand(pool, (char *)&randnum, sizeof(randnum)); + sasl_randfree(&pool); + + time(&now); + + if (hostflag && conn->serverFQDN) + snprintf(buf,maxlen, "<%lu.%lu@%s>", randnum, (unsigned long)now, conn->serverFQDN); /* don't care much about time 32bit overlap */ + else + snprintf(buf,maxlen, "<%lu.%lu>", randnum, (unsigned long)now); + + return (int) strlen(buf); +} + + /* borrowed from larry. probably works :) + * probably is also in acap server somewhere + */ +int sasl_utf8verify(const char *str, unsigned len) +{ + unsigned i; + for (i = 0; i < len; i++) { + /* how many octets? */ + int seqlen = 0; + while (str[i] & (0x80 >> seqlen)) ++seqlen; + if (seqlen == 0) continue; /* this is a valid US-ASCII char */ + if (seqlen == 1) return SASL_BADPROT; /* this shouldn't happen here */ + if (seqlen > 6) return SASL_BADPROT; /* illegal */ + while (--seqlen) + if ((str[++i] & 0xC0) != 0x80) return SASL_BADPROT; /* needed a 10 octet */ + } + return SASL_OK; +} + +/* + * To see why this is really bad see RFC 1750 + * + * unfortunatly there currently is no way to make + * cryptographically secure pseudo random numbers + * without specialized hardware etc... + * thus, this is for nonce use only + */ +void getranddata(unsigned short ret[RPOOL_SIZE]) +{ + long curtime; + + memset(ret, 0, RPOOL_SIZE*sizeof(unsigned short)); + +#ifdef DEV_RANDOM + { + int fd; + + fd = open(DEV_RANDOM, O_RDONLY); + if(fd != -1) { + unsigned char *buf = (unsigned char *)ret; + ssize_t bytesread = 0; + size_t bytesleft = RPOOL_SIZE*sizeof(unsigned short); + + do { + bytesread = read(fd, buf, bytesleft); + if(bytesread == -1 && errno == EINTR) continue; + else if(bytesread <= 0) break; + bytesleft -= bytesread; + buf += bytesread; + } while(bytesleft != 0); + + close(fd); + } + } +#endif + +#ifdef HAVE_GETPID + ret[0] ^= (unsigned short) getpid(); +#endif + +#ifdef HAVE_GETTIMEOFDAY + { + struct timeval tv; + + /* xxx autoconf macro */ +#ifdef _SVID_GETTOD + if (!gettimeofday(&tv)) +#else + if (!gettimeofday(&tv, NULL)) +#endif + { + /* longs are guaranteed to be at least 32 bits; we need + 16 bits in each short */ + ret[0] ^= (unsigned short) (tv.tv_sec & 0xFFFF); + ret[1] ^= (unsigned short) (clock() & 0xFFFF); + ret[1] ^= (unsigned short) (tv.tv_usec >> 16); + ret[2] ^= (unsigned short) (tv.tv_usec & 0xFFFF); + return; + } + } +#endif /* HAVE_GETTIMEOFDAY */ + + /* if all else fails just use time() */ + curtime = (long) time(NULL); /* better be at least 32 bits */ + + ret[0] ^= (unsigned short) (curtime >> 16); + ret[1] ^= (unsigned short) (curtime & 0xFFFF); + ret[2] ^= (unsigned short) (clock() & 0xFFFF); + + return; +} + +int sasl_randcreate(sasl_rand_t **rpool) +{ + (*rpool)=sasl_ALLOC(sizeof(sasl_rand_t)); + if ((*rpool) == NULL) return SASL_NOMEM; + + /* init is lazy */ + (*rpool)->initialized = 0; + + return SASL_OK; +} + +void sasl_randfree(sasl_rand_t **rpool) +{ + sasl_FREE(*rpool); +} + +void sasl_randseed (sasl_rand_t *rpool, const char *seed, unsigned len) +{ + /* is it acceptable to just use the 1st 3 char's given??? */ + unsigned int lup; + + /* check params */ + if (seed == NULL) return; + if (rpool == NULL) return; + + rpool->initialized = 1; + + if (len > sizeof(unsigned short)*RPOOL_SIZE) + len = sizeof(unsigned short)*RPOOL_SIZE; + + for (lup = 0; lup < len; lup += 2) + rpool->pool[lup/2] = (seed[lup] << 8) + seed[lup + 1]; +} + +static void randinit(sasl_rand_t *rpool) +{ + if (!rpool) return; + + if (!rpool->initialized) { + getranddata(rpool->pool); + rpool->initialized = 1; +#if !(defined(WIN32)||defined(macintosh)) +#ifndef HAVE_JRAND48 + { + /* xxx varies by platform */ + unsigned int *foo = (unsigned int *)rpool->pool; + srandom(*foo); + } +#endif /* HAVE_JRAND48 */ +#elif defined(WIN32) + { + unsigned int *foo = (unsigned int *)rpool->pool; + srand(*foo); + } +#endif /* WIN32 */ + } + +} + +void sasl_rand (sasl_rand_t *rpool, char *buf, unsigned len) +{ + unsigned int lup; +#if defined(WIN32) && !defined(__MINGW32__) + unsigned int randomValue; +#endif + + /* check params */ + if (!rpool || !buf) return; + + /* init if necessary */ + randinit(rpool); + + for (lup = 0; lup < len; lup++) { +#if defined(__MINGW32__) + buf[lup] = (char) (rand() >> 8); +#elif defined(WIN32) + if (rand_s(&randomValue) != 0) { + randomValue = rand(); + } + + buf[lup] = (char) (randomValue >> 8); +#elif defined(macintosh) + buf[lup] = (char) (rand() >> 8); +#else /* !WIN32 && !macintosh */ +#ifdef HAVE_JRAND48 + buf[lup] = (char) (jrand48(rpool->pool) >> 8); +#else + buf[lup] = (char) (random() >> 8); +#endif /* HAVE_JRAND48 */ +#endif /* WIN32 */ + } +} + +/* this function is just a bad idea all around, since we're not trying to + implement a true random number generator */ +void sasl_churn (sasl_rand_t *rpool, const char *data, unsigned len) +{ + unsigned int lup; + + /* check params */ + if (!rpool || !data) return; + + /* init if necessary */ + randinit(rpool); + + for (lup=0; lup<len; lup++) + rpool->pool[lup % RPOOL_SIZE] ^= data[lup]; +} + +void sasl_erasebuffer(char *buf, unsigned len) { + memset(buf, 0, len); +} + +/* Lowercase string in place */ +char *sasl_strlower ( + char *val +) +{ + int i; + + if (val == NULL) { + return (NULL); + } + +/* don't use tolower(), as it is locale dependent */ + + for (i = 0; val[i] != '\0'; i++) { + if (val[i] >= 'A' && val[i] <= 'Z') { + val[i] = val[i] - 'A' + 'a'; + } + } + + return (val); +} + +/* A version of gethostname that tries hard to return a FQDN */ +int get_fqhostname( + char *name, + int namelen, + int abort_if_no_fqdn +) +{ + int return_value; + struct addrinfo hints; + struct addrinfo *result; + + return_value = gethostname (name, namelen); + name[namelen-1] = '\0'; /* insure string is always 0 terminated*/ + if (return_value != 0) { + return (return_value); + } + + if (strchr (name, '.') != NULL) { + goto LOWERCASE; + } + +/* gethostname hasn't returned a FQDN, we have to canonify it ourselves */ + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; /* TCP only */ +/* A value of zero for ai_protocol indicates the caller will accept any protocol. or IPPROTO_TCP? */ + hints.ai_protocol = 0; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + hints.ai_addrlen = 0; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + if (getaddrinfo(name, + NULL, /* don't care abour service/port */ + &hints, + &result) != 0) { + if (abort_if_no_fqdn) { + /* errno on Unix, WSASetLastError on Windows are already done by the function */ + return (-1); + } else { + goto LOWERCASE; + } + } + + if (result == NULL || result->ai_canonname == NULL + || strchr (result->ai_canonname, '.') == NULL + || strlen (result->ai_canonname) > namelen -1) { + freeaddrinfo (result); + if (abort_if_no_fqdn) { +#ifdef WIN32 + WSASetLastError (WSANO_DATA); +#elif defined(ENODATA) + errno = ENODATA; +#elif defined(EADDRNOTAVAIL) + errno = EADDRNOTAVAIL; +#endif + return (-1); + } else { + goto LOWERCASE; + } + } + + strncpy (name, result->ai_canonname, namelen); + name[namelen-1] = '\0'; /* insure string is always 0 terminated*/ + freeaddrinfo (result); + +LOWERCASE: + sasl_strlower (name); + return (0); +} + +#if defined(WIN32) && !defined(__MINGW64_VERSION_MAJOR) +/***************************************************************************** + * + * MODULE NAME : GETOPT.C + * + * COPYRIGHTS: + * This module contains code made available by IBM + * Corporation on an AS IS basis. Any one receiving the + * module is considered to be licensed under IBM copyrights + * to use the IBM-provided source code in any way he or she + * deems fit, including copying it, compiling it, modifying + * it, and redistributing it, with or without + * modifications. No license under any IBM patents or + * patent applications is to be implied from this copyright + * license. + * + * A user of the module should understand that IBM cannot + * provide technical support for the module and will not be + * responsible for any consequences of use of the program. + * + * Any notices, including this one, are not to be removed + * from the module without the prior written consent of + * IBM. + * + * AUTHOR: Original author: + * G. R. Blair (BOBBLAIR at AUSVM1) + * Internet: bobblair@bobblair.austin.ibm.com + * + * Extensively revised by: + * John Q. Walker II, Ph.D. (JOHHQ at RALVM6) + * Internet: johnq@ralvm6.vnet.ibm.com + * + *****************************************************************************/ + +/****************************************************************************** + * getopt() + * + * The getopt() function is a command line parser. It returns the next + * option character in argv that matches an option character in opstring. + * + * The argv argument points to an array of argc+1 elements containing argc + * pointers to character strings followed by a null pointer. + * + * The opstring argument points to a string of option characters; if an + * option character is followed by a colon, the option is expected to have + * an argument that may or may not be separated from it by white space. + * The external variable optarg is set to point to the start of the option + * argument on return from getopt(). + * + * The getopt() function places in optind the argv index of the next argument + * to be processed. The system initializes the external variable optind to + * 1 before the first call to getopt(). + * + * When all options have been processed (that is, up to the first nonoption + * argument), getopt() returns EOF. The special option "--" may be used to + * delimit the end of the options; EOF will be returned, and "--" will be + * skipped. + * + * The getopt() function returns a question mark (?) when it encounters an + * option character not included in opstring. This error message can be + * disabled by setting opterr to zero. Otherwise, it returns the option + * character that was detected. + * + * If the special option "--" is detected, or all options have been + * processed, EOF is returned. + * + * Options are marked by either a minus sign (-) or a slash (/). + * + * No errors are defined. + *****************************************************************************/ + +#include <string.h> /* for strchr() */ + +/* static (global) variables that are specified as exported by getopt() */ +__declspec(dllexport) char *optarg = NULL; /* pointer to the start of the option argument */ +__declspec(dllexport) int optind = 1; /* number of the next argv[] to be evaluated */ +__declspec(dllexport) int opterr = 1; /* non-zero if a question mark should be returned */ + + +/* handle possible future character set concerns by putting this in a macro */ +#define _next_char(string) (char)(*(string+1)) + +int getopt(int argc, char *argv[], char *opstring) +{ + static char *pIndexPosition = NULL; /* place inside current argv string */ + char *pArgString = NULL; /* where to start from next */ + char *pOptString; /* the string in our program */ + + + if (pIndexPosition != NULL) { + /* we last left off inside an argv string */ + if (*(++pIndexPosition)) { + /* there is more to come in the most recent argv */ + pArgString = pIndexPosition; + } + } + + if (pArgString == NULL) { + /* we didn't leave off in the middle of an argv string */ + if (optind >= argc) { + /* more command-line arguments than the argument count */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all command-line arguments */ + } + + /*--------------------------------------------------------------------- + * If the next argv[] is not an option, there can be no more options. + *-------------------------------------------------------------------*/ + pArgString = argv[optind++]; /* set this to the next argument ptr */ + + if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */ + ('-' != *pArgString)) { + --optind; /* point to current arg once we're done */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all the command-line flags */ + } + + /* check for special end-of-flags markers */ + if ((strcmp(pArgString, "-") == 0) || + (strcmp(pArgString, "--") == 0)) { + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* encountered the special flag */ + } + + pArgString++; /* look past the / or - */ + } + + if (':' == *pArgString) { /* is it a colon? */ + /*--------------------------------------------------------------------- + * Rare case: if opterr is non-zero, return a question mark; + * otherwise, just return the colon we're on. + *-------------------------------------------------------------------*/ + return (opterr ? (int)'?' : (int)':'); + } + else if ((pOptString = strchr(opstring, *pArgString)) == 0) { + /*--------------------------------------------------------------------- + * The letter on the command-line wasn't any good. + *-------------------------------------------------------------------*/ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return (opterr ? (int)'?' : (int)*pArgString); + } + else { + /*--------------------------------------------------------------------- + * The letter on the command-line matches one we expect to see + *-------------------------------------------------------------------*/ + if (':' == _next_char(pOptString)) { /* is the next letter a colon? */ + /* It is a colon. Look for an argument string. */ + if ('\0' != _next_char(pArgString)) { /* argument in this argv? */ + optarg = &pArgString[1]; /* Yes, it is */ + } + else { + /*------------------------------------------------------------- + * The argument string must be in the next argv. + * But, what if there is none (bad input from the user)? + * In that case, return the letter, and optarg as NULL. + *-----------------------------------------------------------*/ + if (optind < argc) + optarg = argv[optind++]; + else { + optarg = NULL; + return (opterr ? (int)'?' : (int)*pArgString); + } + } + pIndexPosition = NULL; /* not in the middle of anything */ + } + else { + /* it's not a colon, so just return the letter */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = pArgString; /* point to the letter we're on */ + } + return (int)*pArgString; /* return the letter that matched */ + } +} + +#ifndef PASSWORD_MAX +# define PASSWORD_MAX 255 +#endif + +#include <conio.h> +char * +getpass(prompt) +const char *prompt; +{ + register char *p; + register int c; + static char pbuf[PASSWORD_MAX]; + + fprintf(stderr, "%s", prompt); (void) fflush(stderr); + for (p=pbuf; (c = _getch())!=13 && c!=EOF;) { + if (p < &pbuf[sizeof(pbuf)-1]) + *p++ = (char) c; + } + *p = '\0'; + fprintf(stderr, "\n"); (void) fflush(stderr); + return(pbuf); +} + + + +#endif /* WIN32 */ diff --git a/contrib/libs/sasl/lib/server.c b/contrib/libs/sasl/lib/server.c new file mode 100644 index 0000000000..bff461f825 --- /dev/null +++ b/contrib/libs/sasl/lib/server.c @@ -0,0 +1,2406 @@ +/* SASL server API implementation + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* local functions/structs don't start with sasl + */ +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#ifndef macintosh +#include <sys/types.h> +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <string.h> +#include <ctype.h> + +#include "sasl.h" +#include "saslint.h" +#include "saslplug.h" +#include "saslutil.h" + +#define DEFAULT_CHECKPASS_MECH "auxprop" + +/* Contains functions: + * + * sasl_server_init + * sasl_server_new + * sasl_listmech + * sasl_server_start + * sasl_server_step + * sasl_checkpass + * sasl_checkapop + * sasl_user_exists + * sasl_setpass + */ + +/* if we've initialized the server sucessfully */ +static int _sasl_server_active = 0; + +/* For access by other modules */ +int _is_sasl_server_active(void) { return _sasl_server_active; } + +static int _sasl_checkpass(sasl_conn_t *conn, + const char *user, unsigned userlen, + const char *pass, unsigned passlen); + +static mech_list_t *mechlist = NULL; /* global var which holds the list */ + +static sasl_global_callbacks_t global_callbacks; + +/* set the password for a user + * conn -- SASL connection + * user -- user name + * pass -- plaintext password, may be NULL to remove user + * passlen -- length of password, 0 = strlen(pass) + * oldpass -- NULL will sometimes work + * oldpasslen -- length of password, 0 = strlen(oldpass) + * flags -- see flags below + * + * returns: + * SASL_NOCHANGE -- proper entry already exists + * SASL_NOMECH -- no authdb supports password setting as configured + * SASL_NOVERIFY -- user exists, but no settable password present + * SASL_DISABLED -- account disabled + * SASL_PWLOCK -- password locked + * SASL_WEAKPASS -- password too weak for security policy + * SASL_NOUSERPASS -- user-supplied passwords not permitted + * SASL_FAIL -- OS error + * SASL_BADPARAM -- password too long + * SASL_OK -- successful + */ + +int sasl_setpass(sasl_conn_t *conn, + const char *user, + const char *pass, + unsigned passlen, + const char *oldpass, + unsigned oldpasslen, + unsigned flags) +{ + int result = SASL_OK, tmpresult; + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; + const char *password_request[] = { SASL_AUX_PASSWORD_PROP, NULL }; + const char *user_delete_request[] = { SASL_AUX_PASSWORD_PROP, SASL_AUX_ALL, NULL }; + sasl_server_userdb_setpass_t *setpass_cb = NULL; + void *context = NULL; + int tried_setpass = 0; + int failed = 0; + mechanism_t *sm; + server_sasl_mechanism_t *m; + char *current_mech; + + if (!_sasl_server_active || !mechlist) return SASL_NOTINIT; + + /* check params */ + if (!conn) return SASL_BADPARAM; + if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn); + + if ((!(flags & SASL_SET_DISABLE) && passlen == 0) + || ((flags & SASL_SET_CREATE) && (flags & SASL_SET_DISABLE))) + PARAMERROR(conn); + + /* Check that we have an active SASL mechanism */ + if (sasl_getprop (conn, + SASL_MECHNAME, + (const void **) ¤t_mech) != SASL_OK) { + current_mech = NULL; + } + + if ( (flags & SASL_SET_CURMECH_ONLY) && + (current_mech == NULL) ) { + sasl_seterror( conn, SASL_NOLOG, + "No current SASL mechanism available"); + RETURN(conn, SASL_BADPARAM); + } + + /* Do we want to store SASL_AUX_PASSWORD_PROP (plain text)? and + * Do we have an auxprop backend that can store properties? + */ + if ((flags & SASL_SET_DISABLE || !(flags & SASL_SET_NOPLAIN)) && + sasl_auxprop_store(NULL, NULL, NULL) == SASL_OK) { + + tried_setpass++; + + if (flags & SASL_SET_DISABLE) { + pass = NULL; + passlen = 0; + result = prop_request(s_conn->sparams->propctx, user_delete_request); + } else { + result = prop_request(s_conn->sparams->propctx, password_request); + } + if (result == SASL_OK) { + /* NOTE: When deleting users, this will work in a backward compatible way */ + result = prop_set(s_conn->sparams->propctx, SASL_AUX_PASSWORD_PROP, + pass, passlen); + } + if (result == SASL_OK && flags & SASL_SET_DISABLE) { + result = prop_set(s_conn->sparams->propctx, SASL_AUX_ALL, + NULL, 0); + } + if (result == SASL_OK) { + result = sasl_auxprop_store(conn, s_conn->sparams->propctx, user); + } + if (result != SASL_OK) { + _sasl_log(conn, SASL_LOG_ERR, + "setpass failed for %s: %z", + user, result); + failed++; + } else { + _sasl_log(conn, SASL_LOG_NOTE, + "setpass succeeded for %s", user); + } + } + + /* We want to preserve the current value of result, so we use tmpresult below */ + + /* call userdb callback function */ + tmpresult = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_SETPASS, + (sasl_callback_ft *)&setpass_cb, &context); + if (tmpresult == SASL_OK && setpass_cb) { + + tried_setpass++; + + tmpresult = setpass_cb(conn, context, user, pass, passlen, + s_conn->sparams->propctx, flags); + if(tmpresult != SASL_OK) { + if (tmpresult == SASL_CONSTRAINT_VIOLAT) { + if (result == SASL_OK) { + result = tmpresult; + } + } else { + result = tmpresult; + } + _sasl_log(conn, SASL_LOG_ERR, + "setpass callback failed for %s: %z", + user, tmpresult); + failed++; + } else { + _sasl_log(conn, SASL_LOG_NOTE, + "setpass callback succeeded for %s", user); + } + } + + /* now we let the mechanisms set their secrets */ + for (sm = s_conn->mech_list; sm; sm = sm->next) { + m = &sm->m; + + if (!m->plug->setpass) { + /* can't set pass for this mech */ + continue; + } + + /* Invoke only one setpass for the currently selected mechanism, + if SASL_SET_CURMECH_ONLY is specified */ + if ((flags & SASL_SET_CURMECH_ONLY) && + (strcmp(current_mech, m->plug->mech_name) != 0)) { + continue; + } + + tried_setpass++; + + tmpresult = m->plug->setpass(m->plug->glob_context, + ((sasl_server_conn_t *)conn)->sparams, + user, + pass, + passlen, + oldpass, oldpasslen, + flags); + if (tmpresult == SASL_OK) { + _sasl_log(conn, SASL_LOG_NOTE, + "%s: set secret for %s", m->plug->mech_name, user); + + m->condition = SASL_OK; /* if we previously thought the + mechanism didn't have any user secrets + we now think it does */ + + } else if (tmpresult == SASL_NOCHANGE) { + _sasl_log(conn, SASL_LOG_NOTE, + "%s: secret not changed for %s", m->plug->mech_name, user); + } else if (tmpresult == SASL_CONSTRAINT_VIOLAT) { + _sasl_log(conn, SASL_LOG_ERR, + "%s: failed to set secret for %s: constrain violation", + m->plug->mech_name, user); + if (result == SASL_OK) { + result = tmpresult; + } + failed++; + } else { + result = tmpresult; + _sasl_log(conn, SASL_LOG_ERR, + "%s: failed to set secret for %s: %z (%m)", + m->plug->mech_name, user, tmpresult, +#ifndef WIN32 + errno +#else + GetLastError() +#endif + ); + failed++; + } + } + + if (!tried_setpass) { + _sasl_log(conn, SASL_LOG_WARN, + "secret not changed for %s: " + "no writable auxprop plugin or setpass callback found", + user); + } else if (result == SASL_CONSTRAINT_VIOLAT) { + /* If not all setpass failed with SASL_CONSTRAINT_VIOLAT - + ignore SASL_CONSTRAINT_VIOLAT */ + if (failed < tried_setpass) { + result = SASL_OK; + } + } + + RETURN(conn, result); +} + +/* local mechanism which disposes of server */ +static void server_dispose(sasl_conn_t *pconn) +{ + sasl_server_conn_t *s_conn= (sasl_server_conn_t *) pconn; + context_list_t *cur, *cur_next; + + /* Just sanity check that sasl_server_done wasn't called yet */ + if (_sasl_server_active != 0) { + if (s_conn->mech) { + void (*mech_dispose)(void *conn_context, const sasl_utils_t *utils); + + mech_dispose = s_conn->mech->m.plug->mech_dispose; + + if (mech_dispose) { + mech_dispose(pconn->context, s_conn->sparams->utils); + } + } + pconn->context = NULL; + + for(cur = s_conn->mech_contexts; cur; cur=cur_next) { + cur_next = cur->next; + if (cur->context) { + cur->mech->m.plug->mech_dispose(cur->context, s_conn->sparams->utils); + } + sasl_FREE(cur); + } + s_conn->mech_contexts = NULL; + } + + _sasl_free_utils(&s_conn->sparams->utils); + + if (s_conn->sparams->propctx) { + prop_dispose(&s_conn->sparams->propctx); + } + + if (s_conn->appname) { + sasl_FREE(s_conn->appname); + } + + if (s_conn->user_realm) { + sasl_FREE(s_conn->user_realm); + } + + if (s_conn->sparams) { + sasl_FREE(s_conn->sparams); + } + + if (s_conn->mech_list != mechlist->mech_list) { + /* free connection-specific mech_list */ + mechanism_t *m, *prevm; + + m = s_conn->mech_list; /* m point to beginning of the list */ + + while (m) { + prevm = m; + m = m->next; + sasl_FREE(prevm); + } + } + + _sasl_conn_dispose(pconn); +} + +static int init_mechlist(void) +{ + sasl_utils_t *newutils = NULL; + + /* set util functions - need to do rest */ + newutils = _sasl_alloc_utils(NULL, &global_callbacks); + if (newutils == NULL) + return SASL_NOMEM; + + newutils->checkpass = &_sasl_checkpass; + + mechlist->utils = newutils; + mechlist->mech_list = NULL; + mechlist->mech_length = 0; + + return SASL_OK; +} + +static int mech_compare(const sasl_server_plug_t *a, + const sasl_server_plug_t *b) +{ + unsigned sec_diff; + unsigned features_diff; + + /* XXX the following is fairly arbitrary, but its independent + of the order in which the plugins are loaded + */ + sec_diff = a->security_flags ^ b->security_flags; + if (sec_diff & a->security_flags & SASL_SEC_NOANONYMOUS) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOANONYMOUS) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NOPLAINTEXT) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOPLAINTEXT) return -1; + if (sec_diff & a->security_flags & SASL_SEC_MUTUAL_AUTH) return 1; + if (sec_diff & b->security_flags & SASL_SEC_MUTUAL_AUTH) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NOACTIVE) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOACTIVE) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NODICTIONARY) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NODICTIONARY) return -1; + if (sec_diff & a->security_flags & SASL_SEC_FORWARD_SECRECY) return 1; + if (sec_diff & b->security_flags & SASL_SEC_FORWARD_SECRECY) return -1; + + features_diff = a->features ^ b->features; + if (features_diff & a->features & SASL_FEAT_CHANNEL_BINDING) return 1; + if (features_diff & b->features & SASL_FEAT_CHANNEL_BINDING) return -1; + + if (a->max_ssf > b->max_ssf) return 1; + if (a->max_ssf < b->max_ssf) return -1; + + if (SASL_GET_HASH_STRENGTH(a->security_flags) > SASL_GET_HASH_STRENGTH(b->security_flags)) return 1; + if (SASL_GET_HASH_STRENGTH(a->security_flags) < SASL_GET_HASH_STRENGTH(b->security_flags)) return -1; + + return 0; +} + +/* + * parameters: + * p - entry point + */ +int sasl_server_add_plugin(const char *plugname, + sasl_server_plug_init_t *p) +{ + int plugcount; + sasl_server_plug_t *pluglist = NULL; + sasl_server_plug_init_t *entry_point = NULL; + int result; + int version; + int lupe; + + if(!plugname || !p) return SASL_BADPARAM; + + entry_point = (sasl_server_plug_init_t *)p; + + /* call into the shared library asking for information about it */ + /* version is filled in with the version of the plugin */ + result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION, &version, + &pluglist, &plugcount); + + if ((result != SASL_OK) && (result != SASL_NOUSER) + && (result != SASL_CONTINUE)) { + _sasl_log(NULL, SASL_LOG_DEBUG, + "%s_client_plug_init() failed in sasl_server_add_plugin(): %z\n", + plugname, result); + return result; + } + + /* Make sure plugin is using the same SASL version as us */ + if (version != SASL_SERVER_PLUG_VERSION) + { + _sasl_log(NULL, + SASL_LOG_ERR, + "version mismatch on sasl_server_add_plugin for '%s': %d expected, but %d reported", + plugname, + SASL_SERVER_PLUG_VERSION, + version); + return SASL_BADVERS; + } + + for (lupe=0;lupe < plugcount ;lupe++, pluglist++) + { + mechanism_t *mech, *mp; + + mech = sasl_ALLOC(sizeof(mechanism_t)); + if (! mech) return SASL_NOMEM; + memset (mech, 0, sizeof(mechanism_t)); + + mech->m.plug = pluglist; + if(_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) { + sasl_FREE(mech); + return SASL_NOMEM; + } + mech->m.version = version; + + /* whether this mech actually has any users in it's db */ + mech->m.condition = result; /* SASL_OK, SASL_CONTINUE or SASL_NOUSER */ + + /* mech->m.f = NULL; */ + + /* sort mech_list by relative "strength" */ + mp = mechlist->mech_list; + if (!mp || mech_compare(pluglist, mp->m.plug) >= 0) { + /* add mech to head of list */ + mech->next = mechlist->mech_list; + mechlist->mech_list = mech; + } else { + /* find where to insert mech into list */ + while (mp->next && + mech_compare(pluglist, mp->next->m.plug) <= 0) mp = mp->next; + mech->next = mp->next; + mp->next = mech; + } + mechlist->mech_length++; + } + + return SASL_OK; +} + +int sasl_server_done(void) +{ + int result = SASL_CONTINUE; + + if (_sasl_server_cleanup_hook == NULL && _sasl_client_cleanup_hook == NULL) { + return SASL_NOTINIT; + } + + if (_sasl_server_cleanup_hook) { + result = _sasl_server_cleanup_hook(); + + if (result == SASL_OK) { + _sasl_server_idle_hook = NULL; + _sasl_server_cleanup_hook = NULL; + } else { + return result; + } + } + + if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { + return result; + } + + sasl_common_done(); + + return SASL_OK; +} + +static int server_done(void) { + mechanism_t *m; + mechanism_t *prevm; + + if(_sasl_server_active == 0) + return SASL_NOTINIT; + else + _sasl_server_active--; + + if(_sasl_server_active) { + /* Don't de-init yet! Our refcount is nonzero. */ + return SASL_CONTINUE; + } + + if (mechlist != NULL) + { + m=mechlist->mech_list; /* m point to beginning of the list */ + + while (m!=NULL) + { + prevm=m; + m=m->next; + + if (prevm->m.plug->mech_free) { + prevm->m.plug->mech_free(prevm->m.plug->glob_context, + mechlist->utils); + } + + sasl_FREE(prevm->m.plugname); + sasl_FREE(prevm); + } + _sasl_free_utils(&mechlist->utils); + sasl_FREE(mechlist); + mechlist = NULL; + } + + /* Free the auxprop plugins */ + _sasl_auxprop_free(); + + global_callbacks.callbacks = NULL; + global_callbacks.appname = NULL; + + sasl_config_done(); + + return SASL_OK; +} + +static int server_idle(sasl_conn_t *conn) +{ + sasl_server_conn_t *s_conn = NULL; + mechanism_t *m; + + if (! mechlist) { + return 0; + } + + if (!conn) + return 1; + s_conn = (sasl_server_conn_t *) conn; + + for (m = s_conn->mech_list; + m != NULL; + m = m->next) { + if (m->m.plug->idle + && m->m.plug->idle(m->m.plug->glob_context, + conn, + s_conn->sparams)) { + return 1; + } + } + + return 0; +} + +static int load_config(const sasl_callback_t *verifyfile_cb) +{ + int result; + const char *path_to_config = NULL; + size_t path_len; + char *config_filename = NULL; + size_t len; + const sasl_callback_t *getconfpath_cb = NULL; + const char * next; + + /* If appname was not provided, behave as if there is no config file + (see also sasl_config_init() */ + if (global_callbacks.appname == NULL) { + return SASL_CONTINUE; + } + + /* get the path to the config file */ + getconfpath_cb = _sasl_find_getconfpath_callback( global_callbacks.callbacks ); + if (getconfpath_cb == NULL) return SASL_BADPARAM; + + /* getconfpath_cb->proc MUST be a sasl_getconfpath_t; if only C had a type + system */ + result = ((sasl_getconfpath_t *)(getconfpath_cb->proc))(getconfpath_cb->context, + (char **) &path_to_config); + if (result != SASL_OK) goto done; + if (path_to_config == NULL) path_to_config = ""; + + next = path_to_config; + + while (next != NULL) { + next = strchr(path_to_config, PATHS_DELIMITER); + + /* length = length of path + '/' + length of appname + ".conf" + 1 + for '\0' */ + + if (next != NULL) { + path_len = next - path_to_config; + next++; /* Skip to the next path */ + } else { + path_len = strlen(path_to_config); + } + + len = path_len + 2 + strlen(global_callbacks.appname) + 5 + 1; + + if (len > PATH_MAX ) { + result = SASL_FAIL; + goto done; + } + + /* construct the filename for the config file */ + config_filename = sasl_ALLOC((unsigned)len); + if (! config_filename) { + result = SASL_NOMEM; + goto done; + } + + snprintf(config_filename, len, "%.*s%c%s.conf", (int)path_len, path_to_config, + HIER_DELIMITER, global_callbacks.appname); + + /* Ask the application if it's safe to use this file */ + result = ((sasl_verifyfile_t *)(verifyfile_cb->proc))(verifyfile_cb->context, + config_filename, SASL_VRFY_CONF); + + /* returns SASL_CONTINUE if the config file doesn't exist */ + if (result == SASL_OK) { + result = sasl_config_init(config_filename); + + if (result != SASL_CONTINUE) { + /* We are done */ + break; + } + } + + if (config_filename) { + sasl_FREE(config_filename); + config_filename = NULL; + } + + path_to_config = next; + } + + done: + if (config_filename) sasl_FREE(config_filename); + + return result; +} + +/* + * Verify that all the callbacks are valid + */ +static int verify_server_callbacks(const sasl_callback_t *callbacks) +{ + if (callbacks == NULL) return SASL_OK; + + while (callbacks->id != SASL_CB_LIST_END) { + if (callbacks->proc==NULL) return SASL_FAIL; + + callbacks++; + } + + return SASL_OK; +} + +static char *grab_field(char *line, char **eofield) +{ + int d = 0; + char *field; + + while (isspace((int) *line)) line++; + + /* find end of field */ + while (line[d] && !isspace(((int) line[d]))) d++; + field = sasl_ALLOC(d + 1); + if (!field) { return NULL; } + memcpy(field, line, d); + field[d] = '\0'; + *eofield = line + d; + + return field; +} + +struct secflag_map_s { + char *name; + int value; +}; + +struct secflag_map_s secflag_map[] = { + { "noplaintext", SASL_SEC_NOPLAINTEXT }, + { "noactive", SASL_SEC_NOACTIVE }, + { "nodictionary", SASL_SEC_NODICTIONARY }, + { "forward_secrecy", SASL_SEC_FORWARD_SECRECY }, + { "noanonymous", SASL_SEC_NOANONYMOUS }, + { "pass_credentials", SASL_SEC_PASS_CREDENTIALS }, + { "mutual_auth", SASL_SEC_MUTUAL_AUTH }, + { NULL, 0x0 } +}; + +static int parse_mechlist_file(const char *mechlistfile) +{ + FILE *f; + char buf[1024]; + char *t, *ptr; + int r = 0; + + f = fopen(mechlistfile, "r"); + if (!f) return SASL_FAIL; + + r = SASL_OK; + while (fgets(buf, sizeof(buf), f) != NULL) { + mechanism_t *n = sasl_ALLOC(sizeof(mechanism_t)); + sasl_server_plug_t *nplug; + + if (n == NULL) { r = SASL_NOMEM; break; } + n->m.version = SASL_SERVER_PLUG_VERSION; + n->m.condition = SASL_CONTINUE; + nplug = sasl_ALLOC(sizeof(sasl_server_plug_t)); + if (nplug == NULL) { r = SASL_NOMEM; break; } + memset(nplug, 0, sizeof(sasl_server_plug_t)); + + /* each line is: + plugin-file WS mech_name WS max_ssf *(WS security_flag) RET + */ + + /* grab file */ + n->m.f = grab_field(buf, &ptr); + + /* grab mech_name */ + nplug->mech_name = grab_field(ptr, &ptr); + + /* grab max_ssf */ + nplug->max_ssf = strtol(ptr, &ptr, 10); + + /* grab security flags */ + while (*ptr != '\n') { + struct secflag_map_s *map; + + /* read security flag */ + t = grab_field(ptr, &ptr); + map = secflag_map; + while (map->name) { + if (!strcasecmp(t, map->name)) { + nplug->security_flags |= map->value; + break; + } + map++; + } + if (!map->name) { + _sasl_log(NULL, SASL_LOG_ERR, + "%s: couldn't identify flag '%s'", + nplug->mech_name, t); + } + free(t); + } + + /* insert mechanism into mechlist */ + n->m.plug = nplug; + n->next = mechlist->mech_list; + mechlist->mech_list = n; + mechlist->mech_length++; + } + + fclose(f); + return r; +} + +/* initialize server drivers, done once per process + * callbacks -- callbacks for all server connections; must include + * getopt callback + * appname -- name of calling application + * (for lower level logging and reading of the configuration file) + * results: + * state -- server state + * returns: + * SASL_OK -- success + * SASL_BADPARAM -- error in config file + * SASL_NOMEM -- memory failure + * SASL_BADVERS -- Mechanism version mismatch + */ + +int sasl_server_init(const sasl_callback_t *callbacks, + const char *appname) +{ + int ret; + const sasl_callback_t *vf; + const char *pluginfile = NULL; +#ifdef PIC + sasl_getopt_t *getopt; + void *context; +#endif + + const add_plugin_list_t ep_list[] = { + { "sasl_server_plug_init", (add_plugin_t *)sasl_server_add_plugin }, + { "sasl_auxprop_plug_init", (add_plugin_t *)sasl_auxprop_add_plugin }, + { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin }, + { NULL, NULL } + }; + + /* lock allocation type */ + _sasl_allocation_locked++; + + /* we require the appname (if present) to be short enough to be a path */ + if (appname != NULL && strlen(appname) >= PATH_MAX) + return SASL_BADPARAM; + + if (_sasl_server_active) { + /* We're already active, just increase our refcount */ + /* xxx do something with the callback structure? */ + _sasl_server_active++; + return SASL_OK; + } + + ret = _sasl_common_init(&global_callbacks); + if (ret != SASL_OK) + return ret; + + /* verify that the callbacks look ok */ + ret = verify_server_callbacks(callbacks); + if (ret != SASL_OK) + return ret; + + global_callbacks.callbacks = callbacks; + + /* A shared library calling sasl_server_init will pass NULL as appname. + This should retain the original appname. */ + if (appname != NULL) { + global_callbacks.appname = appname; + } + + /* If we fail now, we have to call server_done */ + _sasl_server_active = 1; + + /* allocate mechlist and set it to empty */ + mechlist = sasl_ALLOC(sizeof(mech_list_t)); + if (mechlist == NULL) { + server_done(); + return SASL_NOMEM; + } + + ret = init_mechlist(); + if (ret != SASL_OK) { + server_done(); + return ret; + } + + vf = _sasl_find_verifyfile_callback(callbacks); + + /* load config file if applicable */ + ret = load_config(vf); + if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) { + server_done(); + return ret; + } + + /* load internal plugins */ + sasl_server_add_plugin("EXTERNAL", &external_server_plug_init); + +#ifdef PIC + /* delayed loading of plugins? (DSO only, as it doesn't + * make much [any] sense to delay in the static library case) */ + if (_sasl_getcallback(NULL, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) + == SASL_OK) { + /* No sasl_conn_t was given to getcallback, so we provide the + * global callbacks structure */ + ret = getopt(&global_callbacks, NULL, "plugin_list", &pluginfile, NULL); + } +#endif + + if (pluginfile != NULL) { + /* this file should contain a list of plugins available. + we'll load on demand. */ + + /* Ask the application if it's safe to use this file */ + ret = ((sasl_verifyfile_t *)(vf->proc))(vf->context, + pluginfile, + SASL_VRFY_CONF); + if (ret != SASL_OK) { + _sasl_log(NULL, SASL_LOG_ERR, + "unable to load plugin list %s: %z", pluginfile, ret); + } + + if (ret == SASL_OK) { + ret = parse_mechlist_file(pluginfile); + } + } else { + /* load all plugins now */ + ret = _sasl_load_plugins(ep_list, + _sasl_find_getpath_callback(callbacks), + _sasl_find_verifyfile_callback(callbacks)); + } + + if (ret == SASL_OK) { + _sasl_server_cleanup_hook = &server_done; + _sasl_server_idle_hook = &server_idle; + + ret = _sasl_build_mechlist(); + } else { + server_done(); + } + + return ret; +} + +/* + * Once we have the users plaintext password we + * may want to transition them. That is put entries + * for them in the passwd database for other + * stronger mechanism + * + * for example PLAIN -> CRAM-MD5 + */ +static int +_sasl_transition(sasl_conn_t * conn, + const char * pass, + unsigned passlen) +{ + const char *dotrans = "n"; + sasl_getopt_t *getopt; + int result = SASL_OK; + void *context; + unsigned flags = 0; + + if (! conn) + return SASL_BADPARAM; + + if (! conn->oparams.authid) + PARAMERROR(conn); + + /* check if this is enabled: default to false */ + if (_sasl_getcallback(conn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) + { + getopt(context, NULL, "auto_transition", &dotrans, NULL); + if (dotrans == NULL) dotrans = "n"; + } + + + if (!strcmp(dotrans, "noplain")) flags |= SASL_SET_NOPLAIN; + + if (flags || *dotrans == '1' || *dotrans == 'y' || + (*dotrans == 'o' && dotrans[1] == 'n') || *dotrans == 't') { + /* ok, it's on! */ + _sasl_log(conn, SASL_LOG_NOTE, + "transitioning user %s to auxprop database", + conn->oparams.authid); + result = sasl_setpass(conn, + conn->oparams.authid, + pass, + passlen, + NULL, 0, SASL_SET_CREATE | flags); + } + + RETURN(conn,result); +} + + +/* create context for a single SASL connection + * service -- registered name of the service using SASL (e.g. "imap") + * serverFQDN -- Fully qualified domain name of server. NULL means use + * gethostname() or equivalent. + * Useful for multi-homed servers. + * user_realm -- permits multiple user realms on server, NULL = default + * iplocalport -- server IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * ipremoteport -- client IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * callbacks -- callbacks (e.g., authorization, lang, new getopt context) + * flags -- usage flags (see above) + * returns: + * pconn -- new connection context + * + * returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + */ + +int sasl_server_new(const char *service, + const char *serverFQDN, + const char *user_realm, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *callbacks, + unsigned flags, + sasl_conn_t **pconn) +{ + int result; + sasl_server_conn_t *serverconn; + sasl_utils_t *utils; + sasl_getopt_t *getopt; + void *context; + const char *log_level, *auto_trans; + const char *mlist = NULL; + int plus = 0; + + if (_sasl_server_active==0) return SASL_NOTINIT; + if (! pconn) return SASL_FAIL; + if (! service) return SASL_FAIL; + + *pconn=sasl_ALLOC(sizeof(sasl_server_conn_t)); + if (*pconn==NULL) return SASL_NOMEM; + + memset(*pconn, 0, sizeof(sasl_server_conn_t)); + + serverconn = (sasl_server_conn_t *)*pconn; + + /* make sparams */ + serverconn->sparams=sasl_ALLOC(sizeof(sasl_server_params_t)); + if (serverconn->sparams==NULL) + MEMERROR(*pconn); + + memset(serverconn->sparams, 0, sizeof(sasl_server_params_t)); + + (*pconn)->destroy_conn = &server_dispose; + result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_SERVER, + &server_idle, serverFQDN, + iplocalport, ipremoteport, + callbacks, &global_callbacks); + if (result != SASL_OK) + goto done_error; + + + /* set util functions - need to do rest */ + utils=_sasl_alloc_utils(*pconn, &global_callbacks); + if (!utils) { + result = SASL_NOMEM; + goto done_error; + } + + utils->checkpass = &_sasl_checkpass; + + /* Setup the propctx -> We'll assume the default size */ + serverconn->sparams->propctx=prop_new(0); + if(!serverconn->sparams->propctx) { + result = SASL_NOMEM; + goto done_error; + } + + serverconn->sparams->service = (*pconn)->service; + serverconn->sparams->servicelen = (unsigned) strlen((*pconn)->service); + + if (global_callbacks.appname && global_callbacks.appname[0] != '\0') { + result = _sasl_strdup (global_callbacks.appname, + &serverconn->appname, + NULL); + if (result != SASL_OK) { + result = SASL_NOMEM; + goto done_error; + } + serverconn->sparams->appname = serverconn->appname; + serverconn->sparams->applen = (unsigned) strlen(serverconn->sparams->appname); + } else { + serverconn->appname = NULL; + serverconn->sparams->appname = NULL; + serverconn->sparams->applen = 0; + } + + serverconn->sparams->serverFQDN = (*pconn)->serverFQDN; + serverconn->sparams->slen = (unsigned) strlen((*pconn)->serverFQDN); + + if (user_realm) { + result = _sasl_strdup(user_realm, &serverconn->user_realm, NULL); + serverconn->sparams->urlen = (unsigned) strlen(user_realm); + serverconn->sparams->user_realm = serverconn->user_realm; + } else { + serverconn->user_realm = NULL; + /* the sparams is already zeroed */ + } + + serverconn->sparams->callbacks = callbacks; + + log_level = auto_trans = NULL; + if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + getopt(context, NULL, "log_level", &log_level, NULL); + getopt(context, NULL, "auto_transition", &auto_trans, NULL); + getopt(context, NULL, "mech_list", &mlist, NULL); + } + serverconn->sparams->log_level = log_level ? atoi(log_level) : SASL_LOG_ERR; + + serverconn->sparams->utils = utils; + + if (auto_trans && + (*auto_trans == '1' || *auto_trans == 'y' || *auto_trans == 't' || + (*auto_trans == 'o' && auto_trans[1] == 'n') || + !strcmp(auto_trans, "noplain")) && + sasl_auxprop_store(NULL, NULL, NULL) == SASL_OK) { + serverconn->sparams->transition = &_sasl_transition; + } + + /* if we have a mech_list, create ordered list of avail mechs for this conn */ + if (mlist) { + const char *cp; + mechanism_t *mptr, *tail = NULL; + + while (*mlist) { + /* find end of current mech name */ + for (cp = mlist; *cp && !isspace((int) *cp); cp++); + + /* search for mech name in loaded plugins */ + for (mptr = mechlist->mech_list; mptr; mptr = mptr->next) { + const sasl_server_plug_t *plug = mptr->m.plug; + + if (_sasl_is_equal_mech(mlist, plug->mech_name, (size_t) (cp - mlist), &plus)) { + /* found a match */ + break; + } + } + if (mptr) { + mechanism_t *new = sasl_ALLOC(sizeof(mechanism_t)); + if (!new) return SASL_NOMEM; + + memcpy(&new->m, &mptr->m, sizeof(server_sasl_mechanism_t)); + new->next = NULL; + + if (!serverconn->mech_list) { + serverconn->mech_list = new; + tail = serverconn->mech_list; + } + else { + if (tail) + tail->next = new; + tail = new; + } + serverconn->mech_length++; + } + + /* find next mech name */ + mlist = cp; + while (*mlist && isspace((int) *mlist)) mlist++; + } + } + else { + serverconn->mech_list = mechlist->mech_list; + serverconn->mech_length = mechlist->mech_length; + } + + serverconn->sparams->canon_user = &_sasl_canon_user_lookup; + serverconn->sparams->props = serverconn->base.props; + serverconn->sparams->flags = flags; + + if(result == SASL_OK) return SASL_OK; + + done_error: + _sasl_conn_dispose(*pconn); + sasl_FREE(*pconn); + *pconn = NULL; + return result; +} + +/* + * The rule is: + * IF mech strength + external strength < min ssf THEN FAIL. + * We also have to look at the security properties and make sure + * that this mechanism has everything we want. + */ +static int mech_permitted(sasl_conn_t *conn, + mechanism_t *mech) +{ + sasl_server_conn_t *s_conn = (sasl_server_conn_t *)conn; + const sasl_server_plug_t *plug; + int ret; + int myflags; + context_list_t *cur; + context_list_t *mech_context_list_entry = NULL; + void *context = NULL; + sasl_ssf_t minssf = 0; + + if(!conn) return SASL_NOMECH; + + if(! mech || ! mech->m.plug) { + PARAMERROR(conn); + return SASL_NOMECH; + } + + plug = mech->m.plug; + + /* setup parameters for the call to mech_avail */ + s_conn->sparams->serverFQDN=conn->serverFQDN; + s_conn->sparams->service=conn->service; + s_conn->sparams->user_realm=s_conn->user_realm; + s_conn->sparams->props=conn->props; + s_conn->sparams->external_ssf=conn->external.ssf; + + /* Check if we have banished this one already */ + for (cur = s_conn->mech_contexts; cur; cur=cur->next) { + if (cur->mech == mech) { + /* If it's not mech_avail'd, then stop now */ + if (!cur->context) { + return SASL_NOMECH; + } else { + context = cur->context; + mech_context_list_entry = cur; + } + break; + } + } + + if (conn->props.min_ssf < conn->external.ssf) { + minssf = 0; + } else { + minssf = conn->props.min_ssf - conn->external.ssf; + } + + /* Generic mechanism */ + if (plug->max_ssf < minssf) { + sasl_seterror(conn, SASL_NOLOG, + "mech %s is too weak", plug->mech_name); + return SASL_TOOWEAK; /* too weak */ + } + + if (plug->mech_avail + && (ret = plug->mech_avail(plug->glob_context, + s_conn->sparams, + (void **)&context)) != SASL_OK ) { + if (ret == SASL_NOMECH) { + /* Mark this mech as no good for this connection */ + cur = sasl_ALLOC(sizeof(context_list_t)); + if (!cur) { + MEMERROR(conn); + return SASL_NOMECH; + } + cur->context = NULL; + cur->mech = mech; + cur->next = s_conn->mech_contexts; + s_conn->mech_contexts = cur; + } + + /* SASL_NOTDONE might also get us here */ + + /* Error should be set by mech_avail call */ + return SASL_NOMECH; + } else if (context) { + if (mech_context_list_entry != NULL) { + /* Update the context. It shouldn't have changed, but who knows */ + mech_context_list_entry->context = context; + } else { + /* Save this context */ + cur = sasl_ALLOC(sizeof(context_list_t)); + if (!cur) { + MEMERROR(conn); + return SASL_NOMECH; + } + cur->context = context; + cur->mech = mech; + cur->next = s_conn->mech_contexts; + s_conn->mech_contexts = cur; + } + } + + /* Generic mechanism */ + if (plug->max_ssf < minssf) { + sasl_seterror(conn, SASL_NOLOG, "too weak"); + return SASL_TOOWEAK; /* too weak */ + } + + /* if there are no users in the secrets database we can't use this + mechanism */ + if (mech->m.condition == SASL_NOUSER) { + sasl_seterror(conn, 0, "no users in secrets db"); + return SASL_NOMECH; + } + + /* Can it meet our features? */ + if ((conn->flags & SASL_NEED_PROXY) && + !(plug->features & SASL_FEAT_ALLOWS_PROXY)) { + return SASL_NOMECH; + } + if ((conn->flags & SASL_NEED_HTTP) && + !(plug->features & SASL_FEAT_SUPPORTS_HTTP)) { + return SASL_NOMECH; + } + + /* security properties---if there are any flags that differ and are + in what the connection are requesting, then fail */ + + /* special case plaintext */ + myflags = conn->props.security_flags; + + /* if there's an external layer this is no longer plaintext */ + if ((conn->props.min_ssf <= conn->external.ssf) && + (conn->external.ssf > 1)) { + myflags &= ~SASL_SEC_NOPLAINTEXT; + } + + /* do we want to special case SASL_SEC_PASS_CREDENTIALS? nah.. */ + if ((myflags &= (myflags ^ plug->security_flags)) != 0) { + sasl_seterror(conn, SASL_NOLOG, + "security flags do not match required"); + return (myflags & SASL_SEC_NOPLAINTEXT) ? SASL_ENCRYPT : SASL_NOMECH; + } + + /* Check Features */ + if (plug->features & SASL_FEAT_GETSECRET) { + /* We no longer support sasl_server_{get,put}secret */ + sasl_seterror(conn, 0, + "mech %s requires unprovided secret facility", + plug->mech_name); + return SASL_NOMECH; + } + + return SASL_OK; +} + +/* + * make the authorization + * + */ + +static int do_authorization(sasl_server_conn_t *s_conn) +{ + int ret; + sasl_authorize_t *authproc; + void *auth_context; + + /* now let's see if authname is allowed to proxy for username! */ + + /* check the proxy callback */ + if (_sasl_getcallback(&s_conn->base, SASL_CB_PROXY_POLICY, + (sasl_callback_ft *)&authproc, &auth_context) != SASL_OK) { + INTERROR(&s_conn->base, SASL_NOAUTHZ); + } + + ret = authproc(&(s_conn->base), auth_context, + s_conn->base.oparams.user, s_conn->base.oparams.ulen, + s_conn->base.oparams.authid, s_conn->base.oparams.alen, + s_conn->user_realm, + (s_conn->user_realm ? (unsigned) strlen(s_conn->user_realm) : 0), + s_conn->sparams->propctx); + + RETURN(&s_conn->base, ret); +} + + +/* start a mechanism exchange within a connection context + * mech -- the mechanism name client requested + * clientin -- client initial response (NUL terminated), NULL if empty + * clientinlen -- length of initial response + * serverout -- initial server challenge, NULL if done + * (library handles freeing this string) + * serveroutlen -- length of initial server challenge + * output: + * pconn -- the connection negotiation state on success + * + * Same returns as sasl_server_step() or + * SASL_NOMECH if mechanism not available. + */ +int sasl_server_start(sasl_conn_t *conn, + const char *mech, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen) +{ + sasl_server_conn_t *s_conn=(sasl_server_conn_t *) conn; + int result; + context_list_t *cur, **prev; + mechanism_t *m; + size_t mech_len; + int plus = 0; + + if (_sasl_server_active==0) return SASL_NOTINIT; + + /* check parameters */ + if(!conn) return SASL_BADPARAM; + + if (!mech || ((clientin == NULL) && (clientinlen > 0))) + PARAMERROR(conn); + + if (serverout) *serverout = NULL; + if (serveroutlen) *serveroutlen = 0; + + /* make sure mech is valid mechanism + if not return appropriate error */ + m = s_conn->mech_list; + mech_len = strlen(mech); + + while (m != NULL) { + if (_sasl_is_equal_mech(mech, m->m.plug->mech_name, mech_len, &plus)) { + break; + } + + m = m->next; + } + + if (m == NULL) { + sasl_seterror(conn, 0, "Couldn't find mech %s", mech); + result = SASL_NOMECH; + goto done; + } + + /* Make sure that we're willing to use this mech */ + if ((result = mech_permitted(conn, m)) != SASL_OK) { + goto done; + } + + if (m->m.condition == SASL_CONTINUE) { + sasl_server_plug_init_t *entry_point = NULL; + void *library = NULL; + sasl_server_plug_t *pluglist = NULL; + int version, plugcount; + int l = 0; + + /* need to load this plugin */ + result = _sasl_get_plugin(m->m.f, + _sasl_find_verifyfile_callback(global_callbacks.callbacks), + &library); + + if (result == SASL_OK) { + result = _sasl_locate_entry(library, "sasl_server_plug_init", + (void **)&entry_point); + } + + if (result == SASL_OK) { + result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION, + &version, &pluglist, &plugcount); + } + + if (result == SASL_OK) { + /* find the correct mechanism in this plugin */ + for (l = 0; l < plugcount; l++) { + if (!strcasecmp(pluglist[l].mech_name, + m->m.plug->mech_name)) break; + } + if (l == plugcount) { + result = SASL_NOMECH; + } + } + if (result == SASL_OK) { + /* check that the parameters are the same */ + if ((pluglist[l].max_ssf != m->m.plug->max_ssf) || + (pluglist[l].security_flags != m->m.plug->security_flags)) { + _sasl_log(conn, SASL_LOG_ERR, + "%s: security parameters don't match mechlist file", + pluglist[l].mech_name); + result = SASL_NOMECH; + } + } + if (result == SASL_OK) { + /* copy mechlist over */ + sasl_FREE((sasl_server_plug_t *) m->m.plug); + m->m.plug = &pluglist[l]; + m->m.condition = SASL_OK; + } + + if (result != SASL_OK) { + /* The library will eventually be freed, don't sweat it */ + RETURN(conn, result); + } + } + + if (conn->context) { + s_conn->mech->m.plug->mech_dispose(conn->context, + s_conn->sparams->utils); + conn->context = NULL; + } + + /* We used to setup sparams HERE, but now it's done + inside of mech_permitted (which is called above) */ + prev = &s_conn->mech_contexts; + for (cur = *prev; cur; prev=&cur->next,cur=cur->next) { + if (cur->mech == m) { + if (!cur->context) { + sasl_seterror(conn, 0, + "Got past mech_permitted with a disallowed mech!"); + return SASL_NOMECH; + } + /* If we find it, we need to pull cur out of the + list so it won't be freed later! */ + *prev = cur->next; + conn->context = cur->context; + sasl_FREE(cur); + break; + } + } + + s_conn->mech = m; + + if (!conn->context) { + /* Note that we don't hand over a new challenge */ + result = s_conn->mech->m.plug->mech_new(s_conn->mech->m.plug->glob_context, + s_conn->sparams, + NULL, + 0, + &(conn->context)); + } else { + /* the work was already done by mech_avail! */ + result = SASL_OK; + } + + if (result == SASL_OK) { + if (clientin) { + if (s_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) { + /* Remote sent first, but mechanism does not support it. + * RFC 2222 says we fail at this point. */ + sasl_seterror(conn, + 0, + "Remote sent first but mech does not allow it."); + result = SASL_BADPROT; + } else { + /* Mech wants client-first, so let them have it */ + result = sasl_server_step(conn, + clientin, + clientinlen, + serverout, + serveroutlen); + } + } else { + if (s_conn->mech->m.plug->features & SASL_FEAT_WANT_CLIENT_FIRST) { + /* Mech wants client first anyway, so we should do that */ + if (serverout) *serverout = ""; + if (serveroutlen) *serveroutlen = 0; + result = SASL_CONTINUE; + } else { + /* Mech wants server-first, so let them have it */ + result = sasl_server_step(conn, + clientin, + clientinlen, + serverout, + serveroutlen); + } + } + } + + done: + if ( result != SASL_OK + && result != SASL_CONTINUE + && result != SASL_INTERACT) { + if (conn->context) { + s_conn->mech->m.plug->mech_dispose(conn->context, + s_conn->sparams->utils); + conn->context = NULL; + } + conn->oparams.doneflag = 0; + } + + RETURN(conn,result); +} + + +/* perform one step of the SASL exchange + * clientinlen & clientin -- client data + * NULL on first step if no optional client step + * serveroutlen & serverout -- set to the server data to transmit + * to the client in the next step + * (library handles freeing this) + * + * returns: + * SASL_OK -- exchange is complete. + * SASL_CONTINUE -- indicates another step is necessary. + * SASL_TRANS -- entry for user exists, but not for mechanism + * and transition is possible + * SASL_BADPARAM -- service name needed + * SASL_BADPROT -- invalid input from client + * ... + */ + +int sasl_server_step(sasl_conn_t *conn, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen) +{ + int ret; + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */ + + /* check parameters */ + if (_sasl_server_active==0) return SASL_NOTINIT; + if (!conn) return SASL_BADPARAM; + if ((clientin==NULL) && (clientinlen>0)) + PARAMERROR(conn); + + /* If we've already done the last send, return! */ + if (s_conn->sent_last == 1) { + return SASL_OK; + } + + /* Don't do another step if the plugin told us that we're done */ + if (conn->oparams.doneflag) { + _sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag"); + return SASL_FAIL; + } + + if (serverout) *serverout = NULL; + if (serveroutlen) *serveroutlen = 0; + + ret = s_conn->mech->m.plug->mech_step(conn->context, + s_conn->sparams, + clientin, + clientinlen, + serverout, + serveroutlen, + &conn->oparams); + + if (ret == SASL_OK) { + ret = do_authorization(s_conn); + } + + if (ret == SASL_OK) { + /* if we're done, we need to watch out for the following: + * 1. the mech does server-send-last + * 2. the protocol does not + * + * in this case, return SASL_CONTINUE and remember we are done. + */ + if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) { + s_conn->sent_last = 1; + ret = SASL_CONTINUE; + } + if(!conn->oparams.maxoutbuf) { + conn->oparams.maxoutbuf = conn->props.maxbufsize; + } + + /* Validate channel bindings */ + switch (conn->oparams.cbindingdisp) { + case SASL_CB_DISP_NONE: + if (SASL_CB_CRITICAL(s_conn->sparams)) { + sasl_seterror(conn, 0, + "server requires channel binding but client provided none"); + ret = SASL_BADBINDING; + } + break; + case SASL_CB_DISP_WANT: + if (SASL_CB_PRESENT(s_conn->sparams)) { + sasl_seterror(conn, 0, + "client incorrectly assumed server had no channel binding"); + ret = SASL_BADAUTH; + } + break; + case SASL_CB_DISP_USED: + if (!SASL_CB_PRESENT(s_conn->sparams)) { + sasl_seterror(conn, 0, + "client provided channel binding but server had none"); + ret = SASL_BADBINDING; + } else if (strcmp(conn->oparams.cbindingname, + s_conn->sparams->cbinding->name) != 0) { + sasl_seterror(conn, 0, + "client channel binding %s does not match server %s", + conn->oparams.cbindingname, s_conn->sparams->cbinding->name); + ret = SASL_BADBINDING; + } + break; + } + + if (ret == SASL_OK && + (conn->oparams.user == NULL || conn->oparams.authid == NULL)) { + sasl_seterror(conn, 0, + "mech did not call canon_user for both authzid " \ + "and authid"); + ret = SASL_BADPROT; + } + } + + if ( ret != SASL_OK + && ret != SASL_CONTINUE + && ret != SASL_INTERACT) { + if (conn->context) { + s_conn->mech->m.plug->mech_dispose(conn->context, + s_conn->sparams->utils); + conn->context = NULL; + } + conn->oparams.doneflag = 0; + } + + RETURN(conn, ret); +} + +/* returns the length of all the mechanisms + * added up + */ + +static unsigned mech_names_len(mechanism_t *mech_list) +{ + mechanism_t *listptr; + unsigned result = 0; + + for (listptr = mech_list; + listptr; + listptr = listptr->next) + result += (unsigned) strlen(listptr->m.plug->mech_name); + + return result; +} + +/* This returns a list of mechanisms in a NUL-terminated string + * + * The default behavior is to separate with spaces if sep == NULL + */ +int _sasl_server_listmech(sasl_conn_t *conn, + const char *user __attribute__((unused)), + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount) +{ + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */ + int lup; + mechanism_t *listptr; + int ret; + size_t resultlen; + int flag; + const char *mysep; + + /* if there hasn't been a sasl_sever_init() fail */ + if (_sasl_server_active==0) return SASL_NOTINIT; + if (!conn) return SASL_BADPARAM; + if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn); + + if (! result) + PARAMERROR(conn); + + if (plen != NULL) + *plen = 0; + if (pcount != NULL) + *pcount = 0; + + if (sep) { + mysep = sep; + } else { + mysep = " "; + } + + if (!s_conn->mech_list || s_conn->mech_length <= 0) + INTERROR(conn, SASL_NOMECH); + + resultlen = (prefix ? strlen(prefix) : 0) + + (strlen(mysep) * (s_conn->mech_length - 1) * 2) + + (mech_names_len(s_conn->mech_list) * 2) /* including -PLUS variant */ + + (s_conn->mech_length * (sizeof("-PLUS") - 1)) + + (suffix ? strlen(suffix) : 0) + + 1; + + ret = _buf_alloc(&conn->mechlist_buf, + &conn->mechlist_buf_len, resultlen); + if(ret != SASL_OK) MEMERROR(conn); + + if (prefix) + strcpy (conn->mechlist_buf,prefix); + else + *(conn->mechlist_buf) = '\0'; + + listptr = s_conn->mech_list; + + flag = 0; + /* make list */ + for (lup = 0; lup < s_conn->mech_length; lup++) { + /* currently, we don't use the "user" parameter for anything */ + if (mech_permitted(conn, listptr) == SASL_OK) { + + /* + * If the server would never succeed in the authentication of + * the non-PLUS-variant due to policy reasons, it MUST advertise + * only the PLUS-variant. + */ + if ((listptr->m.plug->features & SASL_FEAT_CHANNEL_BINDING) && + SASL_CB_PRESENT(s_conn->sparams)) { + if (pcount != NULL) { + (*pcount)++; + } + if (flag) { + strcat(conn->mechlist_buf, mysep); + } else { + flag = 1; + } + strcat(conn->mechlist_buf, listptr->m.plug->mech_name); + strcat(conn->mechlist_buf, "-PLUS"); + } + + /* + * If the server cannot support channel binding, it SHOULD + * advertise only the non-PLUS-variant. Here, supporting channel + * binding means the underlying SASL mechanism supports it and + * the application has set some channel binding data. + */ + if (!SASL_CB_PRESENT(s_conn->sparams) || + !SASL_CB_CRITICAL(s_conn->sparams)) { + if (pcount != NULL) { + (*pcount)++; + } + if (flag) { + strcat(conn->mechlist_buf, mysep); + } else { + flag = 1; + } + strcat(conn->mechlist_buf, listptr->m.plug->mech_name); + } + } + + listptr = listptr->next; + } + + if (suffix) + strcat(conn->mechlist_buf,suffix); + + if (plen!=NULL) + *plen = (unsigned) strlen(conn->mechlist_buf); + + *result = conn->mechlist_buf; + + return SASL_OK; +} + +sasl_string_list_t *_sasl_server_mechs(void) +{ + mechanism_t *listptr; + sasl_string_list_t *retval = NULL, *next=NULL; + + if(!_sasl_server_active) return NULL; + + /* make list */ + for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) { + next = sasl_ALLOC(sizeof(sasl_string_list_t)); + + if(!next && !retval) return NULL; + else if(!next) { + next = retval->next; + do { + sasl_FREE(retval); + retval = next; + next = retval->next; + } while(next); + return NULL; + } + + next->d = listptr->m.plug->mech_name; + + if(!retval) { + next->next = NULL; + retval = next; + } else { + next->next = retval; + retval = next; + } + } + + return retval; +} + +#define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t')) +static int is_mech(const char *t, const char *m) +{ + size_t sl = strlen(m); + return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl)); +} + +/* returns OK if it's valid */ +static int _sasl_checkpass(sasl_conn_t *conn, + const char *user, + unsigned userlen, + const char *pass, + unsigned passlen) +{ + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; + int result; + sasl_getopt_t *getopt; + sasl_server_userdb_checkpass_t *checkpass_cb; + void *context; + const char *mlist = NULL, *mech = NULL; + struct sasl_verify_password_s *v; + const char *service = conn->service; + + if (!userlen) userlen = (unsigned) strlen(user); + if (!passlen) passlen = (unsigned) strlen(pass); + + /* call userdb callback function, if available */ + result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS, + (sasl_callback_ft *)&checkpass_cb, &context); + if(result == SASL_OK && checkpass_cb) { + result = checkpass_cb(conn, context, user, pass, passlen, + s_conn->sparams->propctx); + if(result == SASL_OK) + return SASL_OK; + } + + /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */ + if (_sasl_getcallback(conn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) + == SASL_OK) { + getopt(context, NULL, "pwcheck_method", &mlist, NULL); + } + + if(!mlist) mlist = DEFAULT_CHECKPASS_MECH; + + result = SASL_NOMECH; + + mech = mlist; + while (*mech && result != SASL_OK) { + for (v = _sasl_verify_password; v->name; v++) { + if(is_mech(mech, v->name)) { + result = v->verify(conn, user, pass, service, + s_conn->user_realm); + break; + } + } + if (result != SASL_OK) { + /* skip to next mech in list */ + while (*mech && !isspace((int) *mech)) mech++; + while (*mech && isspace((int) *mech)) mech++; + } + else if (!is_mech(mech, "auxprop") && s_conn->sparams->transition) { + s_conn->sparams->transition(conn, pass, passlen); + } + } + + if (result == SASL_NOMECH) { + /* no mechanism available ?!? */ + _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier(s) %s", mlist); + } + + if (result != SASL_OK) + sasl_seterror(conn, SASL_NOLOG, "checkpass failed"); + + RETURN(conn, result); +} + +/* check if a plaintext password is valid + * if user is NULL, check if plaintext passwords are enabled + * inputs: + * user -- user to query in current user_domain + * userlen -- length of username, 0 = strlen(user) + * pass -- plaintext password to check + * passlen -- length of password, 0 = strlen(pass) + * returns + * SASL_OK -- success + * SASL_NOMECH -- mechanism not supported + * SASL_NOVERIFY -- user found, but no verifier + * SASL_NOUSER -- user not found + */ +int sasl_checkpass(sasl_conn_t *conn, + const char *user, + unsigned userlen, + const char *pass, + unsigned passlen) +{ + int result; + + if (_sasl_server_active==0) return SASL_NOTINIT; + + /* check if it's just a query if we are enabled */ + if (!user) + return SASL_OK; + + if (!conn) return SASL_BADPARAM; + + /* check params */ + if (pass == NULL) + PARAMERROR(conn); + + /* canonicalize the username */ + result = _sasl_canon_user(conn, user, userlen, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + &(conn->oparams)); + if(result != SASL_OK) RETURN(conn, result); + user = conn->oparams.user; + + /* Check the password and lookup additional properties */ + result = _sasl_checkpass(conn, user, userlen, pass, passlen); + + /* Do authorization */ + if(result == SASL_OK) { + result = do_authorization((sasl_server_conn_t *)conn); + } + + RETURN(conn,result); +} + +/* check if a user exists on server + * conn -- connection context (may be NULL, used to hold last error) + * service -- registered name of the service using SASL (e.g. "imap") + * user_realm -- permits multiple user realms on server, NULL = default + * user -- NUL terminated user name + * + * returns: + * SASL_OK -- success + * SASL_DISABLED -- account disabled [FIXME: currently not detected] + * SASL_NOUSER -- user not found + * SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported] + * SASL_NOMECH -- no mechanisms enabled + * SASL_UNAVAIL -- remote authentication server unavailable, try again later + */ +int sasl_user_exists(sasl_conn_t *conn, + const char *service, + const char *user_realm, + const char *user) +{ + int result=SASL_NOMECH; + const char *mlist = NULL, *mech = NULL; + void *context; + sasl_getopt_t *getopt; + struct sasl_verify_password_s *v; + + /* check params */ + if (_sasl_server_active==0) return SASL_NOTINIT; + if (!conn) return SASL_BADPARAM; + if (!user || conn->type != SASL_CONN_SERVER) + PARAMERROR(conn); + + if(!service) service = conn->service; + + /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */ + if (_sasl_getcallback(conn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) + == SASL_OK) { + getopt(context, NULL, "pwcheck_method", &mlist, NULL); + } + + if(!mlist) mlist = DEFAULT_CHECKPASS_MECH; + + result = SASL_NOMECH; + + mech = mlist; + while (*mech && result != SASL_OK) { + for (v = _sasl_verify_password; v->name; v++) { + if(is_mech(mech, v->name)) { + result = v->verify(conn, user, NULL, service, user_realm); + break; + } + } + if (result != SASL_OK) { + /* skip to next mech in list */ + while (*mech && !isspace((int) *mech)) mech++; + while (*mech && isspace((int) *mech)) mech++; + } + } + + /* Screen out the SASL_BADPARAM response + * we'll get from not giving a password */ + if (result == SASL_BADPARAM) { + result = SASL_OK; + } + + if (result == SASL_NOMECH) { + /* no mechanism available ?!? */ + _sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?"); + sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?"); + } + + RETURN(conn, result); +} + +/* check if an apop exchange is valid + * (note this is an optional part of the SASL API) + * if challenge is NULL, just check if APOP is enabled + * inputs: + * challenge -- challenge which was sent to client + * challen -- length of challenge, 0 = strlen(challenge) + * response -- client response, "<user> <digest>" (RFC 1939) + * resplen -- length of response, 0 = strlen(response) + * returns + * SASL_OK -- success + * SASL_BADAUTH -- authentication failed + * SASL_BADPARAM -- missing challenge + * SASL_BADPROT -- protocol error (e.g., response in wrong format) + * SASL_NOVERIFY -- user found, but no verifier + * SASL_NOMECH -- mechanism not supported + * SASL_NOUSER -- user not found + */ +int sasl_checkapop(sasl_conn_t *conn, +#ifdef DO_SASL_CHECKAPOP + const char *challenge, + unsigned challen __attribute__((unused)), + const char *response, + unsigned resplen __attribute__((unused))) +#else + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + const char *response __attribute__((unused)), + unsigned resplen __attribute__((unused))) +#endif +{ +#ifdef DO_SASL_CHECKAPOP + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; + char *user, *user_end; + const char *password_request[] = { SASL_AUX_PASSWORD, NULL }; + size_t user_len; + int result; + + if (_sasl_server_active==0) + return SASL_NOTINIT; + + /* check if it's just a query if we are enabled */ + if(!challenge) + return SASL_OK; + + /* check params */ + if (!conn) return SASL_BADPARAM; + if (!response) + PARAMERROR(conn); + + /* Parse out username and digest. + * + * Per RFC 1939, response must be "<user> <digest>", where + * <digest> is a 16-octet value which is sent in hexadecimal + * format, using lower-case ASCII characters. + */ + user_end = strrchr(response, ' '); + if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32) + { + sasl_seterror(conn, 0, "Bad Digest"); + RETURN(conn,SASL_BADPROT); + } + + user_len = (size_t)(user_end - response); + user = sasl_ALLOC(user_len + 1); + memcpy(user, response, user_len); + user[user_len] = '\0'; + + result = prop_request(s_conn->sparams->propctx, password_request); + if(result != SASL_OK) + { + sasl_FREE(user); + RETURN(conn, result); + } + + /* erase the plaintext password */ + s_conn->sparams->utils->prop_erase(s_conn->sparams->propctx, + password_request[0]); + + /* canonicalize the username and lookup any associated properties */ + result = _sasl_canon_user_lookup (conn, + user, + user_len, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + &(conn->oparams)); + sasl_FREE(user); + + if(result != SASL_OK) RETURN(conn, result); + + /* Do APOP verification */ + result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid, + challenge, user_end + 1, s_conn->user_realm); + + /* Do authorization */ + if(result == SASL_OK) { + result = do_authorization((sasl_server_conn_t *)conn); + } else { + /* If verification failed, we don't want to encourage getprop to work */ + conn->oparams.user = NULL; + conn->oparams.authid = NULL; + } + + RETURN(conn, result); +#else /* sasl_checkapop was disabled at compile time */ + sasl_seterror(conn, SASL_NOLOG, + "sasl_checkapop called, but was disabled at compile time"); + RETURN(conn, SASL_NOMECH); +#endif /* DO_SASL_CHECKAPOP */ +} + +/* It would be nice if we can show other information like Author, Company, Year, plugin version */ +static void +_sasl_print_mechanism ( + server_sasl_mechanism_t *m, + sasl_info_callback_stage_t stage, + void *rock __attribute__((unused)) +) +{ + char delimiter; + + if (stage == SASL_INFO_LIST_START) { + printf ("List of server plugins follows\n"); + return; + } else if (stage == SASL_INFO_LIST_END) { + return; + } + + /* Process the mechanism */ + printf ("Plugin \"%s\" ", m->plugname); + + switch (m->condition) { + case SASL_OK: + printf ("[loaded]"); + break; + + case SASL_CONTINUE: + printf ("[delayed]"); + break; + + case SASL_NOUSER: + printf ("[no users]"); + break; + + default: + printf ("[unknown]"); + break; + } + + printf (", \tAPI version: %d\n", m->version); + + if (m->plug != NULL) { + printf ("\tSASL mechanism: %s, best SSF: %d, supports setpass: %s\n", + m->plug->mech_name, + m->plug->max_ssf, + (m->plug->setpass != NULL) ? "yes" : "no" + ); + + + printf ("\tsecurity flags:"); + + delimiter = ' '; + if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) { + printf ("%cNO_ANONYMOUS", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) { + printf ("%cNO_PLAINTEXT", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NOACTIVE) { + printf ("%cNO_ACTIVE", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NODICTIONARY) { + printf ("%cNO_DICTIONARY", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) { + printf ("%cFORWARD_SECRECY", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) { + printf ("%cPASS_CREDENTIALS", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) { + printf ("%cMUTUAL_AUTH", delimiter); + delimiter = '|'; + } + + + + printf ("\n\tfeatures:"); + + delimiter = ' '; + if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) { + printf ("%cWANT_CLIENT_FIRST", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_SERVER_FIRST) { + printf ("%cSERVER_FIRST", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) { + printf ("%cPROXY_AUTHENTICATION", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_DONTUSE_USERPASSWD) { + printf ("%cDONTUSE_USERPASSWD", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) { + printf ("%cNEED_SERVER_FQDN", delimiter); + delimiter = '|'; + } + + /* Is this one used? */ + if (m->plug->features & SASL_FEAT_SERVICE) { + printf ("%cSERVICE", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_GETSECRET) { + printf ("%cNEED_GETSECRET", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_GSS_FRAMING) { + printf ("%cGSS_FRAMING", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) { + printf ("%cCHANNEL_BINDING", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_SUPPORTS_HTTP) { + printf ("%cSUPPORTS_HTTP", delimiter); + delimiter = '|'; + } + } + + if (m->f) { + printf ("\n\twill be loaded from \"%s\"", m->f); + } + + printf ("\n"); +} + +/* Dump information about available server plugins (separate functions should be + used for canon and auxprop plugins */ +int sasl_server_plugin_info ( + const char *c_mech_list, /* space separated mechanism list or NULL for ALL */ + sasl_server_info_callback_t *info_cb, + void *info_cb_rock +) +{ + mechanism_t *m; + server_sasl_mechanism_t plug_data; + char * cur_mech; + char *mech_list = NULL; + char * p; + + if (info_cb == NULL) { + info_cb = _sasl_print_mechanism; + } + + if (mechlist != NULL) { + info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock); + + if (c_mech_list == NULL) { + m = mechlist->mech_list; /* m point to beginning of the list */ + + while (m != NULL) { + memcpy (&plug_data, &m->m, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + + m = m->next; + } + } else { + mech_list = strdup(c_mech_list); + + cur_mech = mech_list; + + while (cur_mech != NULL) { + p = strchr (cur_mech, ' '); + if (p != NULL) { + *p = '\0'; + p++; + } + + m = mechlist->mech_list; /* m point to beginning of the list */ + + while (m != NULL) { + if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) { + memcpy (&plug_data, &m->m, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + } + + m = m->next; + } + + cur_mech = p; + } + + free (mech_list); + } + + info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock); + + return (SASL_OK); + } + + return (SASL_NOTINIT); +} diff --git a/contrib/libs/sasl/lib/seterror.c b/contrib/libs/sasl/lib/seterror.c new file mode 100644 index 0000000000..05eec9a8e2 --- /dev/null +++ b/contrib/libs/sasl/lib/seterror.c @@ -0,0 +1,263 @@ +/* seterror.c - sasl_seterror split out because glue libraries + * can't pass varargs lists + * Rob Siemborski + * Tim Martin + * split from common.c by Rolf Braun + */ + +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#ifdef HAVE_SYSLOG +#include <syslog.h> +#endif +#include <stdarg.h> +#include <ctype.h> + +#include <sasl.h> +#include <saslutil.h> +#include <saslplug.h> +#include "saslint.h" + +#ifdef WIN32 +/* need to handle the fact that errno has been defined as a function + in a dll, not an extern int */ +# ifdef errno +# undef errno +# endif /* errno */ +#endif /* WIN32 */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/* this is apparently no longer a user function */ +static int _sasl_seterror_usererr(int saslerr) +{ + /* Hide the difference in a username failure and a password failure */ + if (saslerr == SASL_NOUSER) + return SASL_BADAUTH; + + /* otherwise return the error given; no transform necessary */ + return saslerr; +} + +/* set the error string which will be returned by sasl_errdetail() using + * syslog()-style formatting (e.g. printf-style with %m as the string form + * of an errno error) + * + * primarily for use by server callbacks such as the sasl_authorize_t + * callback and internally to plug-ins + * + * This will also trigger a call to the SASL logging callback (if any) + * with a level of SASL_LOG_FAIL unless the SASL_NOLOG flag is set. + * + * Messages should be sensitive to the current language setting. If there + * is no SASL_CB_LANGUAGE callback messages MUST be US-ASCII otherwise UTF-8 + * is used and use of RFC 2482 for mixed-language text is encouraged. + * + * if conn is NULL, function does nothing + */ +void sasl_seterror(sasl_conn_t *conn, + unsigned flags, + const char *fmt, ...) +{ + size_t outlen=0; /* current length of output buffer */ + size_t pos = 0; /* current position in format string */ + size_t formatlen; + int result; + sasl_log_t *log_cb = NULL; + void *log_ctx; + int ival; + char *cval; + va_list ap; /* varargs thing */ + char **error_buf; + size_t *error_buf_len; + + if(!conn) { +#ifndef SASL_OSX_CFMGLUE + if(!(flags & SASL_NOLOG)) { + /* See if we have a logging callback... */ + result = _sasl_getcallback(NULL, SASL_CB_LOG, (sasl_callback_ft *)&log_cb, &log_ctx); + if (result == SASL_OK && ! log_cb) + result = SASL_FAIL; + if (result != SASL_OK) + return; + + log_cb(log_ctx, SASL_LOG_FAIL, + "No sasl_conn_t passed to sasl_seterror"); + } +#endif /* SASL_OSX_CFMGLUE */ + return; + } else if(!fmt) return; + +/* we need to use a back end function to get the buffer because the + cfm glue can't be rooting around in the internal structs */ + _sasl_get_errorbuf(conn, &error_buf, &error_buf_len); + + formatlen = strlen(fmt); + + va_start(ap, fmt); /* start varargs */ + + while(pos<formatlen) + { + if (fmt[pos]!='%') /* regular character */ + { + result = _buf_alloc(error_buf, error_buf_len, outlen+1); + if (result != SASL_OK) + goto done; + (*error_buf)[outlen]=fmt[pos]; + outlen++; + pos++; + } else { /* formating thing */ + int done=0; + char frmt[10]; + int frmtpos=1; + char tempbuf[21]; + frmt[0]='%'; + pos++; + + while (done==0) + { + switch(fmt[pos]) + { + case 's': /* need to handle this */ + cval = va_arg(ap, char *); /* get the next arg */ + result = _sasl_add_string(error_buf, error_buf_len, + &outlen, cval); + + if (result != SASL_OK) /* add the string */ + goto done; + + done=1; + break; + + case '%': /* double % output the '%' character */ + result = _buf_alloc(error_buf, error_buf_len, outlen+1); + if (result != SASL_OK) + goto done; + (*error_buf)[outlen]='%'; + outlen++; + done=1; + break; + + case 'm': /* insert the errno string */ + result = _sasl_add_string(error_buf, error_buf_len, + &outlen, + strerror(va_arg(ap, int))); + if (result != SASL_OK) + goto done; + done=1; + break; + + case 'z': /* insert the sasl error string */ + result = _sasl_add_string(error_buf, error_buf_len, &outlen, + (char *)sasl_errstring(_sasl_seterror_usererr( + va_arg(ap, int)),NULL,NULL)); + if (result != SASL_OK) + goto done; + done=1; + break; + + case 'c': + frmt[frmtpos++]=fmt[pos]; + frmt[frmtpos]=0; + tempbuf[0] = (char) va_arg(ap, int); /* get the next arg */ + tempbuf[1]='\0'; + + /* now add the character */ + result = _sasl_add_string(error_buf, error_buf_len, + &outlen, tempbuf); + if (result != SASL_OK) + goto done; + done=1; + break; + + case 'd': + case 'i': + frmt[frmtpos++]=fmt[pos]; + frmt[frmtpos]=0; + ival = va_arg(ap, int); /* get the next arg */ + + snprintf(tempbuf,20,frmt,ival); /* have snprintf do the work */ + /* now add the string */ + result = _sasl_add_string(error_buf, error_buf_len, + &outlen, tempbuf); + if (result != SASL_OK) + goto done; + done=1; + + break; + default: + frmt[frmtpos++]=fmt[pos]; /* add to the formating */ + frmt[frmtpos]=0; + if (frmtpos>9) + done=1; + } + pos++; + if (pos>formatlen) + done=1; + } + + } + } + + (*error_buf)[outlen]='\0'; /* put 0 at end */ + +#ifndef SASL_OSX_CFMGLUE + if(!(flags & SASL_NOLOG)) { + /* See if we have a logging callback... */ + result = _sasl_getcallback(conn, SASL_CB_LOG, (sasl_callback_ft *)&log_cb, &log_ctx); + if (result == SASL_OK && ! log_cb) + result = SASL_FAIL; + if (result != SASL_OK) + goto done; + + result = log_cb(log_ctx, SASL_LOG_FAIL, conn->error_buf); + } +#endif /* SASL_OSX_CFMGLUE */ +done: + va_end(ap); +} diff --git a/contrib/libs/sasl/lib/staticopen.h b/contrib/libs/sasl/lib/staticopen.h new file mode 100644 index 0000000000..d1983163d9 --- /dev/null +++ b/contrib/libs/sasl/lib/staticopen.h @@ -0,0 +1,188 @@ +/* staticopen.h + * Rob Siemborski + * Howard Chu + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +typedef enum { + UNKNOWN = 0, SERVER = 1, CLIENT = 2, AUXPROP = 3, CANONUSER = 4 +} _sasl_plug_type; + +typedef struct { + _sasl_plug_type type; + char *name; + sasl_client_plug_init_t *plug; +} _sasl_plug_rec; + +/* For static linking */ +#define SPECIFIC_CLIENT_PLUG_INIT_PROTO( x ) \ +sasl_client_plug_init_t x##_client_plug_init + +#define SPECIFIC_SERVER_PLUG_INIT_PROTO( x ) \ +sasl_server_plug_init_t x##_server_plug_init + +#define SPECIFIC_AUXPROP_PLUG_INIT_PROTO( x ) \ +sasl_auxprop_init_t x##_auxprop_plug_init + +#define SPECIFIC_CANONUSER_PLUG_INIT_PROTO( x ) \ +sasl_canonuser_init_t x##_canonuser_plug_init + +/* Static Compillation Foo */ +#define SPECIFIC_CLIENT_PLUG_INIT( x, n )\ + { CLIENT, n, x##_client_plug_init } +#define SPECIFIC_SERVER_PLUG_INIT( x, n )\ + { SERVER, n, (sasl_client_plug_init_t *)x##_server_plug_init } +#define SPECIFIC_AUXPROP_PLUG_INIT( x, n )\ + { AUXPROP, n, (sasl_client_plug_init_t *)x##_auxprop_plug_init } +#define SPECIFIC_CANONUSER_PLUG_INIT( x, n )\ + { CANONUSER, n, (sasl_client_plug_init_t *)x##_canonuser_plug_init } + +#ifdef STATIC_ANONYMOUS +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( anonymous ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( anonymous ); +#endif +#ifdef STATIC_CRAMMD5 +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( crammd5 ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( crammd5 ); +#endif +#ifdef STATIC_DIGESTMD5 +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( digestmd5 ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( digestmd5 ); +#endif +#ifdef STATIC_SCRAM +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( scram ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( scram ); +#endif +#ifdef STATIC_GSSAPIV2 +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( gssapiv2 ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( gssapiv2 ); +#endif +#ifdef STATIC_KERBEROS4 +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( kerberos4 ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( kerberos4 ); +#endif +#ifdef STATIC_LOGIN +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( login ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( login ); +#endif +#ifdef STATIC_NTLM +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( ntlm ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( ntlm ); +#endif +#ifdef STATIC_OTP +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( otp ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( otp ); +#endif +#ifdef STATIC_PLAIN +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( plain ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( plain ); +#endif +#ifdef STATIC_SRP +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( srp ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( srp ); +#endif +#ifdef STATIC_SASLDB +extern SPECIFIC_AUXPROP_PLUG_INIT_PROTO( sasldb ); +#endif +#ifdef STATIC_SQL +extern SPECIFIC_AUXPROP_PLUG_INIT_PROTO( sql ); +#endif +#ifdef STATIC_LDAPDB +extern SPECIFIC_AUXPROP_PLUG_INIT_PROTO( ldapdb ); +#endif + +_sasl_plug_rec _sasl_static_plugins[] = { +#ifdef STATIC_ANONYMOUS + SPECIFIC_SERVER_PLUG_INIT( anonymous, "ANONYMOUS" ), + SPECIFIC_CLIENT_PLUG_INIT( anonymous, "ANONYMOUS" ), +#endif +#ifdef STATIC_CRAMMD5 + SPECIFIC_SERVER_PLUG_INIT( crammd5, "CRAM-MD5" ), + SPECIFIC_CLIENT_PLUG_INIT( crammd5, "CRAM-MD5" ), +#endif +#ifdef STATIC_DIGESTMD5 + SPECIFIC_SERVER_PLUG_INIT( digestmd5, "DIGEST-MD5" ), + SPECIFIC_CLIENT_PLUG_INIT( digestmd5, "DIGEST-MD5" ), +#endif +#ifdef STATIC_GSSAPIV2 + SPECIFIC_SERVER_PLUG_INIT( gssapiv2, "GSSAPI" ), + SPECIFIC_CLIENT_PLUG_INIT( gssapiv2, "GSSAPI" ), +#endif +#ifdef STATIC_KERBEROS4 + SPECIFIC_SERVER_PLUG_INIT( kerberos4, "KERBEROS_V4" ), + SPECIFIC_CLIENT_PLUG_INIT( kerberos4, "KERBEROS_V4" ), +#endif +#ifdef STATIC_LOGIN + SPECIFIC_SERVER_PLUG_INIT( login, "LOGIN" ), + SPECIFIC_CLIENT_PLUG_INIT( login, "LOGIN" ), +#endif +#ifdef STATIC_NTLM + SPECIFIC_SERVER_PLUG_INIT( ntlm, "NTLM" ), + SPECIFIC_CLIENT_PLUG_INIT( ntlm, "NTLM" ), +#endif +#ifdef STATIC_OTP + SPECIFIC_SERVER_PLUG_INIT( otp, "OTP" ), + SPECIFIC_CLIENT_PLUG_INIT( otp, "OTP" ), +#endif +#ifdef STATIC_PLAIN + SPECIFIC_SERVER_PLUG_INIT( plain, "PLAIN" ), + SPECIFIC_CLIENT_PLUG_INIT( plain, "PLAIN" ), +#endif +#ifdef STATIC_SCRAM + SPECIFIC_SERVER_PLUG_INIT( scram, "SCRAM" ), + SPECIFIC_CLIENT_PLUG_INIT( scram, "SCRAM" ), +#endif +#ifdef STATIC_SRP + SPECIFIC_SERVER_PLUG_INIT( srp, "SRP" ), + SPECIFIC_CLIENT_PLUG_INIT( srp, "SRP" ), +#endif +#ifdef STATIC_SASLDB + SPECIFIC_AUXPROP_PLUG_INIT( sasldb, "SASLDB" ), +#endif +#ifdef STATIC_SQL + SPECIFIC_AUXPROP_PLUG_INIT( sql, "SQL" ), +#endif +#ifdef STATIC_LDAPDB + SPECIFIC_AUXPROP_PLUG_INIT( ldapdb, "LDAPDB" ), +#endif + { UNKNOWN, NULL, NULL } +}; diff --git a/contrib/libs/sasl/plugins/anonymous.c b/contrib/libs/sasl/plugins/anonymous.c new file mode 100644 index 0000000000..a57266a746 --- /dev/null +++ b/contrib/libs/sasl/plugins/anonymous.c @@ -0,0 +1,387 @@ +/* Anonymous SASL plugin + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sasl.h> +#include <saslplug.h> + +#include "plugin_common.h" + +#ifdef macintosh +#error #include <sasl_anonymous_plugin_decl.h> +#endif + +/***************************** Common Section *****************************/ + +static const char anonymous_id[] = "anonymous"; + +/***************************** Server Section *****************************/ + +static int +anonymous_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + /* holds state are in */ + if (!conn_context) { + PARAMERROR( sparams->utils ); + return SASL_BADPARAM; + } + + *conn_context = NULL; + + return SASL_OK; +} + +static int +anonymous_server_mech_step(void *conn_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + char *clientdata; + int result; + + if (!sparams + || !serverout + || !serveroutlen + || !oparams) { + if (sparams) PARAMERROR( sparams->utils ); + return SASL_BADPARAM; + } + + *serverout = NULL; + *serveroutlen = 0; + + if (!clientin) { + return SASL_CONTINUE; + } + + /* We force a truncation 255 characters (specified by RFC 2245) */ + if (clientinlen > 255) clientinlen = 255; + + /* NULL-terminate the clientin... */ + clientdata = sparams->utils->malloc(clientinlen + 1); + if (!clientdata) { + MEMERROR(sparams->utils); + return SASL_NOMEM; + } + + strncpy(clientdata, clientin, clientinlen); + clientdata[clientinlen] = '\0'; + + sparams->utils->log(sparams->utils->conn, + SASL_LOG_NOTE, + "ANONYMOUS login: \"%s\"", + clientdata); + + if (clientdata != clientin) + sparams->utils->free(clientdata); + + result = sparams->canon_user(sparams->utils->conn, + anonymous_id, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + + if (result != SASL_OK) return result; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static sasl_server_plug_t anonymous_server_plugins[] = +{ + { + "ANONYMOUS", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_DONTUSE_USERPASSWD, /* features */ + NULL, /* glob_context */ + &anonymous_server_mech_new, /* mech_new */ + &anonymous_server_mech_step, /* mech_step */ + NULL, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech_avail */ + NULL /* spare */ + } +}; + +int anonymous_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { + SETERROR( utils, "ANONYMOUS version mismatch" ); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = anonymous_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + char *out_buf; + unsigned out_buf_len; +} client_context_t; + +static int +anonymous_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *cparams, + void **conn_context) +{ + client_context_t *text; + + if (!conn_context) { + PARAMERROR(cparams->utils); + return SASL_BADPARAM; + } + + /* holds state are in */ + text = cparams->utils->malloc(sizeof(client_context_t)); + if (text == NULL) { + MEMERROR(cparams->utils); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(client_context_t)); + + *conn_context = text; + + return SASL_OK; +} + +static int +anonymous_client_mech_step(void *conn_context, + sasl_client_params_t *cparams, + const char *serverin __attribute__((unused)), + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + client_context_t *text = (client_context_t *) conn_context; + size_t userlen; + char hostname[256]; + const char *user = NULL; + int user_result = SASL_OK; + int result; + + if (!cparams + || !clientout + || !clientoutlen + || !oparams) { + if (cparams) PARAMERROR( cparams->utils ); + return SASL_BADPARAM; + } + + *clientout = NULL; + *clientoutlen = 0; + + if (serverinlen != 0) { + SETERROR( cparams->utils, + "Nonzero serverinlen in ANONYMOUS continue_step" ); + return SASL_BADPROT; + } + + /* check if sec layer strong enough */ + if (cparams->props.min_ssf > cparams->external_ssf) { + SETERROR( cparams->utils, "SSF requested of ANONYMOUS plugin"); + return SASL_TOOWEAK; + } + + /* try to get the trace info */ + if (user == NULL) { + user_result = _plug_get_userid(cparams->utils, &user, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { + return user_result; + } + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + cparams->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if (user_result == SASL_INTERACT) { + /* make the prompt list */ + result = + _plug_make_prompts(cparams->utils, prompt_need, + "Please enter anonymous identification", + "", + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (result != SASL_OK) return result; + + return SASL_INTERACT; + } + + if (!user || !*user) { + user = anonymous_id; + } + userlen = strlen(user); + + result = cparams->canon_user(cparams->utils->conn, + anonymous_id, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + + memset(hostname, 0, sizeof(hostname)); + gethostname(hostname, sizeof(hostname)); + hostname[sizeof(hostname)-1] = '\0'; + + *clientoutlen = (unsigned) (userlen + strlen(hostname) + 1); + + result = _plug_buf_alloc(cparams->utils, &text->out_buf, + &text->out_buf_len, *clientoutlen); + + if (result != SASL_OK) return result; + + strcpy(text->out_buf, user); + text->out_buf[userlen] = '@'; + /* use memcpy() instead of strcpy() so we don't add the NUL */ + memcpy(text->out_buf + userlen + 1, hostname, strlen(hostname)); + + *clientout = text->out_buf; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static void anonymous_client_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + client_context_t *text = (client_context_t *) conn_context; + + if(!text) return; + + if (text->out_buf) utils->free(text->out_buf); + + utils->free(text); +} + +static const unsigned long anonymous_required_prompts[] = { + SASL_CB_LIST_END +}; + +static sasl_client_plug_t anonymous_client_plugins[] = +{ + { + "ANONYMOUS", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST, /* features */ + anonymous_required_prompts, /* required_prompts */ + NULL, /* glob_context */ + &anonymous_client_mech_new, /* mech_new */ + &anonymous_client_mech_step, /* mech_step */ + &anonymous_client_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int anonymous_client_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR( utils, "ANONYMOUS version mismatch" ); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = anonymous_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/plugins/cram.c b/contrib/libs/sasl/plugins/cram.c new file mode 100644 index 0000000000..4d19a64068 --- /dev/null +++ b/contrib/libs/sasl/plugins/cram.c @@ -0,0 +1,687 @@ +/* CRAM-MD5 SASL plugin + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#ifndef macintosh +#include <sys/stat.h> +#endif +#include <fcntl.h> + +#include <sasl.h> +#include <saslplug.h> +#include <saslutil.h> + +#include "plugin_common.h" + +#ifdef macintosh +#error #include <sasl_cram_plugin_decl.h> +#endif + +/***************************** Common Section *****************************/ + +/* convert a string of 8bit chars to it's representation in hex + * using lowercase letters + */ +static char *convert16(unsigned char *in, int inlen, const sasl_utils_t *utils) +{ + static char hex[]="0123456789abcdef"; + int lup; + char *out; + + out = utils->malloc(inlen*2+1); + if (out == NULL) return NULL; + + for (lup=0; lup < inlen; lup++) { + out[lup*2] = hex[in[lup] >> 4]; + out[lup*2+1] = hex[in[lup] & 15]; + } + + out[lup*2] = 0; + return out; +} + + +/***************************** Server Section *****************************/ + +typedef struct server_context { + int state; + + char *challenge; +} server_context_t; + +static int +crammd5_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + server_context_t *text; + + /* holds state are in */ + text = sparams->utils->malloc(sizeof(server_context_t)); + if (text == NULL) { + MEMERROR( sparams->utils ); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(server_context_t)); + + text->state = 1; + + *conn_context = text; + + return SASL_OK; +} + +/* + * Returns the current time (or part of it) in string form + * maximum length=15 + */ +static char *gettime(sasl_server_params_t *sparams) +{ + char *ret; + time_t t; + + t=time(NULL); + ret= sparams->utils->malloc(15); + if (ret==NULL) return NULL; + + /* the bottom bits are really the only random ones so if + we overflow we don't want to loose them */ + snprintf(ret,15,"%lu",t%(0xFFFFFF)); + + return ret; +} + +static char *randomdigits(sasl_server_params_t *sparams) +{ + unsigned int num; + char *ret; + unsigned char temp[5]; /* random 32-bit number */ + + sparams->utils->rand(sparams->utils->rpool,(char *) temp,4); + num=(temp[0] * 256 * 256 * 256) + + (temp[1] * 256 * 256) + + (temp[2] * 256) + + (temp[3] ); + + ret = sparams->utils->malloc(15); /* there's no way an unsigned can be longer than this right? */ + if (ret == NULL) return NULL; + sprintf(ret, "%u", num); + + return ret; +} + +static int +crammd5_server_mech_step1(server_context_t *text, + sasl_server_params_t *sparams, + const char *clientin __attribute__((unused)), + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams __attribute__((unused))) +{ + char *time, *randdigits; + + /* we shouldn't have received anything */ + if (clientinlen != 0) { + SETERROR(sparams->utils, "CRAM-MD5 does not accept inital data"); + return SASL_BADPROT; + } + + /* get time and a random number for the nonce */ + time = gettime(sparams); + randdigits = randomdigits(sparams); + if ((time == NULL) || (randdigits == NULL)) { + MEMERROR( sparams->utils ); + return SASL_NOMEM; + } + + /* allocate some space for the challenge */ + text->challenge = sparams->utils->malloc(200 + 1); + if (text->challenge == NULL) { + MEMERROR(sparams->utils); + return SASL_NOMEM; + } + + /* create the challenge */ + snprintf(text->challenge, 200, "<%s.%s@%s>", randdigits, time, + sparams->serverFQDN); + + *serverout = text->challenge; + *serveroutlen = (unsigned) strlen(text->challenge); + + /* free stuff */ + sparams->utils->free(time); + sparams->utils->free(randdigits); + + text->state = 2; + + return SASL_CONTINUE; +} + +static int +crammd5_server_mech_step2(server_context_t *text, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout __attribute__((unused)), + unsigned *serveroutlen __attribute__((unused)), + sasl_out_params_t *oparams) +{ + char *userid = NULL; + sasl_secret_t *sec = NULL; + int pos; + size_t len; + int result = SASL_FAIL; + const char *password_request[] = { SASL_AUX_PASSWORD, +#if defined(OBSOLETE_CRAM_ATTR) + "*cmusaslsecretCRAM-MD5", +#endif + NULL }; + struct propval auxprop_values[3]; + HMAC_MD5_CTX tmphmac; + HMAC_MD5_STATE md5state; + int clear_md5state = 0; + char *digest_str = NULL; + SASL_UINT4 digest[4]; + + /* extract userid; everything before last space */ + pos = clientinlen-1; + while ((pos > 0) && (clientin[pos] != ' ')) pos--; + + if (pos <= 0) { + SETERROR( sparams->utils,"need authentication name"); + return SASL_BADPROT; + } + + userid = (char *) sparams->utils->malloc(pos+1); + if (userid == NULL) { + MEMERROR( sparams->utils); + return SASL_NOMEM; + } + + /* copy authstr out */ + memcpy(userid, clientin, pos); + userid[pos] = '\0'; + + result = sparams->utils->prop_request(sparams->propctx, password_request); + if (result != SASL_OK) goto done; + + /* this will trigger the getting of the aux properties */ + result = sparams->canon_user(sparams->utils->conn, + userid, 0, SASL_CU_AUTHID | SASL_CU_AUTHZID, + oparams); + if (result != SASL_OK) goto done; + + result = sparams->utils->prop_getnames(sparams->propctx, + password_request, + auxprop_values); + if (result < 0 || + ((!auxprop_values[0].name || !auxprop_values[0].values) +#if defined(OBSOLETE_CRAM_ATTR) + && (!auxprop_values[1].name || !auxprop_values[1].values) +#endif + )) { + /* We didn't find this username */ + sparams->utils->seterror(sparams->utils->conn,0, + "no secret in database"); + result = sparams->transition ? SASL_TRANS : SASL_NOUSER; + goto done; + } + + if (auxprop_values[0].name && auxprop_values[0].values) { + len = strlen(auxprop_values[0].values[0]); + if (len == 0) { + sparams->utils->seterror(sparams->utils->conn,0, + "empty secret"); + result = SASL_FAIL; + goto done; + } + + sec = sparams->utils->malloc(sizeof(sasl_secret_t) + len); + if (!sec) goto done; + + sec->len = (unsigned) len; + strncpy((char *)sec->data, auxprop_values[0].values[0], len + 1); + + clear_md5state = 1; + /* Do precalculation on plaintext secret */ + sparams->utils->hmac_md5_precalc(&md5state, /* OUT */ + sec->data, + sec->len); +#if defined(OBSOLETE_CRAM_ATTR) + } else if (auxprop_values[1].name && auxprop_values[1].values) { + /* We have a precomputed secret */ + memcpy(&md5state, auxprop_values[1].values[0], + sizeof(HMAC_MD5_STATE)); +#endif + } else { + sparams->utils->seterror(sparams->utils->conn, 0, + "Have neither type of secret"); + return SASL_FAIL; + } + + /* erase the plaintext password */ + sparams->utils->prop_erase(sparams->propctx, password_request[0]); + + /* ok this is annoying: + so we have this half-way hmac transform instead of the plaintext + that means we half to: + -import it back into a md5 context + -do an md5update with the nonce + -finalize it + */ + sparams->utils->hmac_md5_import(&tmphmac, (HMAC_MD5_STATE *) &md5state); + sparams->utils->MD5Update(&(tmphmac.ictx), + (const unsigned char *) text->challenge, + (unsigned) strlen(text->challenge)); + sparams->utils->hmac_md5_final((unsigned char *) &digest, &tmphmac); + + /* convert to base 16 with lower case letters */ + digest_str = convert16((unsigned char *) digest, 16, sparams->utils); + + /* if same then verified + * - we know digest_str is null terminated but clientin might not be + * - verify the length of clientin anyway! + */ + len = strlen(digest_str); + if (clientinlen-pos-1 < len || + strncmp(digest_str, clientin+pos+1, len) != 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "incorrect digest response"); + result = SASL_BADAUTH; + goto done; + } + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + result = SASL_OK; + + done: + if (userid) sparams->utils->free(userid); + if (sec) _plug_free_secret(sparams->utils, &sec); + + if (digest_str) sparams->utils->free(digest_str); + if (clear_md5state) memset(&md5state, 0, sizeof(md5state)); + + return result; +} + +static int crammd5_server_mech_step(void *conn_context, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + server_context_t *text = (server_context_t *) conn_context; + + *serverout = NULL; + *serveroutlen = 0; + + if (text == NULL) { + return SASL_BADPROT; + } + + /* this should be well more than is ever needed */ + if (clientinlen > 1024) { + SETERROR(sparams->utils, "CRAM-MD5 input longer than 1024 bytes"); + return SASL_BADPROT; + } + + switch (text->state) { + + case 1: + return crammd5_server_mech_step1(text, sparams, + clientin, clientinlen, + serverout, serveroutlen, + oparams); + + case 2: + return crammd5_server_mech_step2(text, sparams, + clientin, clientinlen, + serverout, serveroutlen, + oparams); + + default: /* should never get here */ + sparams->utils->log(NULL, SASL_LOG_ERR, + "Invalid CRAM-MD5 server step %d\n", text->state); + return SASL_FAIL; + } + + return SASL_FAIL; /* should never get here */ +} + +static void crammd5_server_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + server_context_t *text = (server_context_t *) conn_context; + + if (!text) return; + + if (text->challenge) _plug_free_string(utils,&(text->challenge)); + + utils->free(text); +} + +static sasl_server_plug_t crammd5_server_plugins[] = +{ + { + "CRAM-MD5", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS, /* security_flags */ + SASL_FEAT_SERVER_FIRST, /* features */ + NULL, /* glob_context */ + &crammd5_server_mech_new, /* mech_new */ + &crammd5_server_mech_step, /* mech_step */ + &crammd5_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech avail */ + NULL /* spare */ + } +}; + +int crammd5_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { + SETERROR( utils, "CRAM version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = crammd5_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + char *out_buf; + unsigned out_buf_len; +} client_context_t; + +static int crammd5_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + client_context_t *text; + + /* holds state are in */ + text = params->utils->malloc(sizeof(client_context_t)); + if (text == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(client_context_t)); + + *conn_context = text; + + return SASL_OK; +} + +static char *make_hashed(sasl_secret_t *sec, char *nonce, int noncelen, + const sasl_utils_t *utils) +{ + unsigned char digest[24]; + char *in16; + + if (sec == NULL) return NULL; + + /* do the hmac md5 hash output 128 bits */ + utils->hmac_md5((unsigned char *) nonce, noncelen, + sec->data, sec->len, digest); + + /* convert that to hex form */ + in16 = convert16(digest, 16, utils); + if (in16 == NULL) return NULL; + + return in16; +} + +static int crammd5_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + client_context_t *text = (client_context_t *) conn_context; + const char *authid = NULL; + sasl_secret_t *password = NULL; + unsigned int free_password = 0; /* set if we need to free password */ + int auth_result = SASL_OK; + int pass_result = SASL_OK; + int result; + size_t maxsize; + char *in16 = NULL; + + *clientout = NULL; + *clientoutlen = 0; + + /* First check for absurd lengths */ + if (serverinlen > 1024) { + params->utils->seterror(params->utils->conn, 0, + "CRAM-MD5 input longer than 1024 bytes"); + return SASL_BADPROT; + } + + /* check if sec layer strong enough */ + if (params->props.min_ssf > params->external_ssf) { + SETERROR( params->utils, "SSF requested of CRAM-MD5 plugin"); + return SASL_TOOWEAK; + } + + /* try to get the userid */ + if (oparams->authid == NULL) { + auth_result=_plug_get_authid(params->utils, &authid, prompt_need); + + if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) + return auth_result; + } + + /* try to get the password */ + if (password == NULL) { + pass_result=_plug_get_password(params->utils, &password, + &free_password, prompt_need); + + if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) + return pass_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if ((auth_result == SASL_INTERACT) || (pass_result == SASL_INTERACT)) { + /* make the prompt list */ + result = + _plug_make_prompts(params->utils, prompt_need, + NULL, NULL, + auth_result == SASL_INTERACT ? + "Please enter your authentication name" : NULL, + NULL, + pass_result == SASL_INTERACT ? + "Please enter your password" : NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (result != SASL_OK) goto cleanup; + + return SASL_INTERACT; + } + + if (!password) { + PARAMERROR(params->utils); + return SASL_BADPARAM; + } + + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) goto cleanup; + + /* + * username SP digest (keyed md5 where key is passwd) + */ + + in16 = make_hashed(password, (char *) serverin, serverinlen, + params->utils); + + if (in16 == NULL) { + SETERROR(params->utils, "whoops, make_hashed failed us this time"); + result = SASL_FAIL; + goto cleanup; + } + + maxsize = 32+1+strlen(oparams->authid)+30; + result = _plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), (unsigned) maxsize); + if (result != SASL_OK) goto cleanup; + + snprintf(text->out_buf, maxsize, "%s %s", oparams->authid, in16); + + *clientout = text->out_buf; + *clientoutlen = (unsigned) strlen(*clientout); + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + result = SASL_OK; + + cleanup: + /* get rid of private information */ + if (in16) _plug_free_string(params->utils, &in16); + + /* get rid of all sensitive info */ + if (free_password) _plug_free_secret(params-> utils, &password); + + return result; +} + +static void crammd5_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + client_context_t *text = (client_context_t *) conn_context; + + if (!text) return; + + if (text->out_buf) utils->free(text->out_buf); + + utils->free(text); +} + +static sasl_client_plug_t crammd5_client_plugins[] = +{ + { + "CRAM-MD5", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS, /* security_flags */ + SASL_FEAT_SERVER_FIRST, /* features */ + NULL, /* required_prompts */ + NULL, /* glob_context */ + &crammd5_client_mech_new, /* mech_new */ + &crammd5_client_mech_step, /* mech_step */ + &crammd5_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int crammd5_client_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR( utils, "CRAM version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = crammd5_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/plugins/digestmd5.c b/contrib/libs/sasl/plugins/digestmd5.c new file mode 100644 index 0000000000..59f87135f6 --- /dev/null +++ b/contrib/libs/sasl/plugins/digestmd5.c @@ -0,0 +1,4778 @@ +/* DIGEST-MD5 SASL plugin + * Ken Murchison + * Rob Siemborski + * Tim Martin + * Alexey Melnikov + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifndef macintosh +#include <sys/types.h> +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <ctype.h> + +/* DES support */ +#ifdef WITH_DES +# ifdef WITH_SSL_DES +# include <openssl/des.h> +# include <openssl/opensslv.h> +# if (OPENSSL_VERSION_NUMBER >= 0x0090700f) && \ + !defined(OPENSSL_ENABLE_OLD_DES_SUPPORT) +# define des_cblock DES_cblock +# define des_key_schedule DES_key_schedule +# define des_key_sched(k,ks) \ + DES_key_sched((k),&(ks)) +# define des_cbc_encrypt(i,o,l,k,iv,e) \ + DES_cbc_encrypt((i),(o),(l),&(k),(iv),(e)) +# define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ + DES_ede2_cbc_encrypt((i),(o),(l),&(k1),&(k2),(iv),(e)) +# endif /* OpenSSL 0.9.7+ w/o old DES support */ +# else /* system DES library */ +#ifdef HAVE_DES_H +# error #include <des.h> +#endif +# endif +#endif /* WITH_DES */ + +#ifdef WIN32 +# include <winsock2.h> +#else /* Unix */ +# include <netinet/in.h> +#endif /* WIN32 */ + +#include <sasl.h> +#include <saslplug.h> + +#include "plugin_common.h" + +#ifndef WIN32 +extern int strcasecmp(const char *s1, const char *s2); +#endif /* end WIN32 */ + +#ifdef macintosh +#error #include <sasl_md5_plugin_decl.h> +#endif + +/* external definitions */ + +#define bool int + +#ifndef TRUE +#define TRUE (1) +#define FALSE (0) +#endif + +/* MAX_UIN32_DIV_10 * 10 + MAX_UIN32_MOD_10 == 2^32-1 == 4294967295 */ +#define MAX_UIN32_DIV_10 429496729 +#define MAX_UIN32_MOD_10 5 + +#define DEFAULT_BUFSIZE 0xFFFF +#define MAX_SASL_BUFSIZE 0xFFFFFF + +/***************************** Common Section *****************************/ + +/* Definitions */ +#define NONCE_SIZE (32) /* arbitrary */ + +/* Layer Flags */ +#define DIGEST_NOLAYER (1) +#define DIGEST_INTEGRITY (2) +#define DIGEST_PRIVACY (4) + +/* defines */ +#define HASHLEN 16 +typedef unsigned char HASH[HASHLEN + 1]; +#define HASHHEXLEN 32 +typedef unsigned char HASHHEX[HASHHEXLEN + 1]; + +#define MAC_SIZE 10 +#define MAC_OFFS 2 + +const char *SEALING_CLIENT_SERVER="Digest H(A1) to client-to-server sealing key magic constant"; +const char *SEALING_SERVER_CLIENT="Digest H(A1) to server-to-client sealing key magic constant"; + +const char *SIGNING_CLIENT_SERVER="Digest session key to client-to-server signing key magic constant"; +const char *SIGNING_SERVER_CLIENT="Digest session key to server-to-client signing key magic constant"; + +#define HT (9) +#define CR (13) +#define LF (10) +#define SP (32) +#define DEL (127) + +#define NEED_ESCAPING "\"\\" + +#define REALM_CHAL_PREFIX "Available realms:" + +static char *quote (char *str); + +struct context; + +/* function definitions for cipher encode/decode */ +typedef int cipher_function_t(struct context *, + const char *, + unsigned, + unsigned char[], + char *, + unsigned *); + +typedef int cipher_init_t(struct context *, unsigned char [16], + unsigned char [16]); +typedef void cipher_free_t(struct context *); + +enum Context_type { SERVER = 0, CLIENT = 1 }; + +typedef struct cipher_context cipher_context_t; + +/* cached auth info used for fast reauth */ +typedef struct reauth_entry { + char *authid; + char *realm; + unsigned char *nonce; + unsigned int nonce_count; + unsigned char *cnonce; + + union { + struct { + time_t timestamp; + } s; /* server stuff */ + + struct { + char *serverFQDN; + int protection; + struct digest_cipher *cipher; + unsigned long server_maxbuf; + + /* for HTTP mode (RFC 2617) only */ + char *algorithm; + unsigned char *opaque; + } c; /* client stuff */ + } u; +} reauth_entry_t; + +typedef struct reauth_cache { + /* static stuff */ + enum Context_type i_am; /* are we the client or server? */ + time_t timeout; + void *mutex; + unsigned size; + + reauth_entry_t *e; /* fixed-size hash table of entries */ +} reauth_cache_t; + +/* global context for reauth use */ +typedef struct digest_glob_context { + reauth_cache_t *reauth; +} digest_glob_context_t; + +/* context that stores info */ +typedef struct context { + int state; /* state in the authentication we are in */ + enum Context_type i_am; /* are we the client or server? */ + int http_mode; /* use RFC 2617 compatible protocol? */ + + reauth_cache_t *reauth; + + char *authid; + char *realm; + unsigned char *nonce; + unsigned int nonce_count; + unsigned char *cnonce; + + /* only used by the client */ + char ** realms; + int realm_cnt; + + char *response_value; + + unsigned int seqnum; + unsigned int rec_seqnum; /* for checking integrity */ + + HASH Ki_send; + HASH Ki_receive; + + HASH HA1; /* Kcc or Kcs */ + + /* copy of utils from the params structures */ + const sasl_utils_t *utils; + + /* For general use */ + char *out_buf; + unsigned out_buf_len; + + /* for encoding/decoding */ + buffer_info_t *enc_in_buf; + char *encode_buf, *decode_buf, *decode_packet_buf; + unsigned encode_buf_len, decode_buf_len, decode_packet_buf_len; + + decode_context_t decode_context; + + /* if privacy mode is used use these functions for encode and decode */ + cipher_function_t *cipher_enc; + cipher_function_t *cipher_dec; + cipher_init_t *cipher_init; + cipher_free_t *cipher_free; + struct cipher_context *cipher_enc_context; + struct cipher_context *cipher_dec_context; +} context_t; + +struct digest_cipher { + char *name; + sasl_ssf_t ssf; + int n; /* bits to make privacy key */ + int flag; /* a bitmask to make things easier for us */ + + cipher_function_t *cipher_enc; + cipher_function_t *cipher_dec; + cipher_init_t *cipher_init; + cipher_free_t *cipher_free; +}; +#if 0 +static const unsigned char *COLON = ":"; +#else +static const unsigned char COLON[] = { ':', '\0' }; +#endif +/* Hashes a string to produce an unsigned short */ +static unsigned hash(const char *str) +{ + unsigned val = 0; + int i; + + while (str && *str) { + i = (int) *str; + val ^= i; + val <<= 1; + str++; + } + + return val; +} + +static void CvtHex(HASH Bin, HASHHEX Hex) +{ + unsigned short i; + unsigned char j; + + for (i = 0; i < HASHLEN; i++) { + j = (Bin[i] >> 4) & 0xf; + if (j <= 9) + Hex[i * 2] = (j + '0'); + else + Hex[i * 2] = (j + 'a' - 10); + j = Bin[i] & 0xf; + if (j <= 9) + Hex[i * 2 + 1] = (j + '0'); + else + Hex[i * 2 + 1] = (j + 'a' - 10); + } + Hex[HASHHEXLEN] = '\0'; +} + +/* + * calculate request-digest/response-digest as per HTTP Digest spec + */ +void +DigestCalcResponse(const sasl_utils_t * utils, + HASHHEX HA1, /* HEX(H(A1)) */ + unsigned char *pszNonce, /* nonce from server */ + unsigned int pszNonceCount, /* 8 hex digits */ + unsigned char *pszCNonce, /* client nonce */ + unsigned char *pszQop, /* qop-value: "", "auth", + * "auth-int" */ + unsigned char *pszDigestUri, /* requested URL */ + unsigned char *pszMethod, + HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ + HASHHEX Response /* request-digest or response-digest */ + ) +{ + MD5_CTX Md5Ctx; + HASH HA2; + HASH RespHash; + HASHHEX HA2Hex; + unsigned char ncvalue[10]; + + /* calculate H(A2) */ + utils->MD5Init(&Md5Ctx); + + if (pszMethod != NULL) { + utils->MD5Update(&Md5Ctx, pszMethod, (unsigned) strlen((char *) pszMethod)); + } + utils->MD5Update(&Md5Ctx, (unsigned char *) COLON, 1); + + /* utils->MD5Update(&Md5Ctx, (unsigned char *) "AUTHENTICATE:", 13); */ + utils->MD5Update(&Md5Ctx, pszDigestUri, (unsigned) strlen((char *) pszDigestUri)); + if (strcasecmp((char *) pszQop, "auth") != 0) { + /* append ":00000000000000000000000000000000" */ + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, HEntity, HASHHEXLEN); + } + utils->MD5Final(HA2, &Md5Ctx); + CvtHex(HA2, HA2Hex); + + /* calculate response */ + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, HA1, HASHHEXLEN); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce)); + utils->MD5Update(&Md5Ctx, COLON, 1); + if (*pszQop) { + sprintf((char *)ncvalue, "%08x", pszNonceCount); + utils->MD5Update(&Md5Ctx, ncvalue, (unsigned) strlen((char *)ncvalue)); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce)); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszQop, (unsigned) strlen((char *) pszQop)); + utils->MD5Update(&Md5Ctx, COLON, 1); + } + utils->MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); + utils->MD5Final(RespHash, &Md5Ctx); + CvtHex(RespHash, Response); +} + +static bool UTF8_In_8859_1(const unsigned char *base, size_t len) +{ + const unsigned char *scan, *end; + + end = base + len; + for (scan = base; scan < end; ++scan) { + if (*scan > 0xC3) + break; /* abort if outside 8859-1 */ + if (*scan >= 0xC0 && *scan <= 0xC3) { + if (++scan == end || *scan < 0x80 || *scan > 0xBF) + break; + } + } + + /* if scan >= end, then this is a 8859-1 string. */ + return (scan >= end); +} + +/* + * if the string is entirely in the 8859-1 subset of UTF-8, then translate to + * 8859-1 prior to MD5 + */ +static void MD5_UTF8_8859_1(const sasl_utils_t * utils, + MD5_CTX * ctx, + bool In_ISO_8859_1, + const unsigned char *base, + int len) +{ + const unsigned char *scan, *end; + unsigned char cbuf; + + end = base + len; + + /* if we found a character outside 8859-1, don't alter string */ + if (!In_ISO_8859_1) { + utils->MD5Update(ctx, base, len); + return; + } + /* convert to 8859-1 prior to applying hash */ + do { + for (scan = base; scan < end && *scan < 0xC0; ++scan); + if (scan != base) + utils->MD5Update(ctx, base, (unsigned) (scan - base)); + if (scan + 1 >= end) + break; + cbuf = ((scan[0] & 0x3) << 6) | (scan[1] & 0x3f); + utils->MD5Update(ctx, &cbuf, 1); + base = scan + 2; + } + while (base < end); +} + +/** + * Returns true if it mangled the username. + */ +static bool DigestCalcSecret(const sasl_utils_t * utils, + unsigned char *pszUserName, + unsigned char *pszRealm, + unsigned char *Password, + int PasswordLen, + bool Ignore_8859, + HASH HA1) +{ + bool In_8859_1; + bool Any_8859_1 = FALSE; + MD5_CTX Md5Ctx; + + /* Chris Newman clarified that the following text in DIGEST-MD5 spec + is bogus: "if name and password are both in ISO 8859-1 charset" + We shoud use code example instead */ + + utils->MD5Init(&Md5Ctx); + + /* We have to convert UTF-8 to ISO-8859-1 if possible */ + if (Ignore_8859 == FALSE) { + In_8859_1 = UTF8_In_8859_1(pszUserName, strlen((char *) pszUserName)); + MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1, + pszUserName, (unsigned) strlen((char *) pszUserName)); + Any_8859_1 |= In_8859_1; + } else { + utils->MD5Update(&Md5Ctx, pszUserName, (unsigned) strlen((char *) pszUserName)); + } + + utils->MD5Update(&Md5Ctx, COLON, 1); + + /* a NULL realm is equivalent to the empty string */ + if (pszRealm != NULL && pszRealm[0] != '\0') { + if (Ignore_8859 == FALSE) { + /* We have to convert UTF-8 to ISO-8859-1 if possible */ + In_8859_1 = UTF8_In_8859_1(pszRealm, strlen((char *) pszRealm)); + MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1, + pszRealm, (unsigned) strlen((char *) pszRealm)); + Any_8859_1 |= In_8859_1; + } else { + utils->MD5Update(&Md5Ctx, pszRealm, (unsigned) strlen((char *) pszRealm)); + } + } + + utils->MD5Update(&Md5Ctx, COLON, 1); + + if (Ignore_8859 == FALSE) { + /* We have to convert UTF-8 to ISO-8859-1 if possible */ + In_8859_1 = UTF8_In_8859_1(Password, PasswordLen); + MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1, + Password, PasswordLen); + Any_8859_1 |= In_8859_1; + } else { + utils->MD5Update(&Md5Ctx, Password, PasswordLen); + } + utils->MD5Final(HA1, &Md5Ctx); + + return Any_8859_1; +} + +static unsigned char *create_nonce(const sasl_utils_t * utils) +{ + unsigned char *base64buf; + int base64len; + + char *ret = (char *) utils->malloc(NONCE_SIZE); + if (ret == NULL) + return NULL; + + utils->rand(utils->rpool, (char *) ret, NONCE_SIZE); + + /* base 64 encode it so it has valid chars */ + base64len = (NONCE_SIZE * 4 / 3) + (NONCE_SIZE % 3 ? 4 : 0); + + base64buf = (unsigned char *) utils->malloc(base64len + 1); + if (base64buf == NULL) { + utils->seterror(utils->conn, 0, "Unable to allocate final buffer"); + return NULL; + } + + /* + * Returns SASL_OK on success, SASL_BUFOVER if result won't fit + */ + if (utils->encode64(ret, NONCE_SIZE, + (char *) base64buf, base64len, NULL) != SASL_OK) { + utils->free(ret); + return NULL; + } + utils->free(ret); + + return base64buf; +} + +static int add_to_challenge(const sasl_utils_t *utils, + char **str, unsigned *buflen, unsigned *curlen, + char *name, + unsigned char *value, + bool need_quotes) +{ + size_t namesize = strlen(name); + size_t valuesize = strlen((char *) value); + unsigned newlen; + int ret; + + newlen = (unsigned) (*curlen + 1 + namesize + 2 + valuesize + 2); + ret = _plug_buf_alloc(utils, str, buflen, newlen); + if(ret != SASL_OK) return ret; + + if (*curlen > 0) { + strcat(*str, ","); + strcat(*str, name); + } else { + strcpy(*str, name); + } + + if (need_quotes) { + strcat(*str, "=\""); + + /* Check if the value needs quoting */ + if (strpbrk ((char *)value, NEED_ESCAPING) != NULL) { + char * quoted = quote ((char *) value); + if (quoted == NULL) + MEMERROR(utils); + valuesize = strlen(quoted); + /* As the quoted string is bigger, make sure we have enough + space now */ + ret = _plug_buf_alloc(utils, str, buflen, newlen); + if (ret == SASL_OK) { + strcat(*str, quoted); + free (quoted); + } else { + free (quoted); + return ret; + } + } else { + strcat(*str, (char *) value); + } + strcat(*str, "\""); + } else { + strcat(*str, "="); + strcat(*str, (char *) value); + } + + *curlen = newlen; + return SASL_OK; +} + +static int is_lws_char (char c) +{ + return (c == ' ' || c == HT || c == CR || c == LF); +} + +static char *skip_lws (char *s) +{ + if (!s) return NULL; + + /* skipping spaces: */ + while (is_lws_char(s[0])) { + if (s[0] == '\0') break; + s++; + } + + return s; +} + +/* Same as skip_lws, but do this right to left */ +/* skip LWSP at the end of the value (if any), skip_r_lws returns pointer to + the first LWSP character, NUL (if there were none) or NULL if the value + is entirely from LWSP characters */ +static char *skip_r_lws (char *s) +{ + char *end; + size_t len; + + if (!s) return NULL; + + len = strlen(s); + if (len == 0) return NULL; + + /* the last character before terminating NUL */ + end = s + len - 1; + + /* skipping spaces: */ + while (end > s && (end[0] == ' ' || end[0] == HT || end[0] == CR || end[0] == LF)) { + end--; + } + + /* If all string from spaces, return NULL */ + if (end == s && (end[0] == ' ' || end[0] == HT || end[0] == CR || end[0] == LF)) { + return NULL; + } else { + return (end + 1); + } +} + +static char *skip_token (char *s, int caseinsensitive) +{ + if(!s) return NULL; + + while (s[0]>SP) { + if (s[0]==DEL || s[0]=='(' || s[0]==')' || s[0]=='<' || s[0]=='>' || + s[0]=='@' || s[0]==',' || s[0]==';' || s[0]==':' || s[0]=='\\' || + s[0]=='\'' || s[0]=='/' || s[0]=='[' || s[0]==']' || s[0]== '?' || + s[0]=='=' || s[0]== '{' || s[0]== '}') { + if (caseinsensitive == 1) { + if (!isupper((unsigned char) s[0])) + break; + } else { + break; + } + } + s++; + } + return s; +} + +/* Convert a string to 32 bit unsigned integer. + Any number of trailing spaces is allowed, but not a string + entirely comprised of spaces */ +static bool str2ul32 (char *str, unsigned long * value) +{ + unsigned int n; + char c; + + if (str == NULL) { + return (FALSE); + } + + *value = 0; + + str = skip_lws (str); + if (str[0] == '\0') { + return (FALSE); + } + + n = 0; + while (str[0] != '\0') { + c = str[0]; + if (!isdigit((int)c)) { + return (FALSE); + } + +/* Will overflow after adding additional digit */ + if (n > MAX_UIN32_DIV_10) { + return (FALSE); + } else if (n == MAX_UIN32_DIV_10 && ((unsigned) (c - '0') > MAX_UIN32_MOD_10)) { + return (FALSE); + } + + n = n * 10 + (unsigned) (c - '0'); + str++; + } + + *value = n; + return (TRUE); +} + +/* NULL - error (unbalanced quotes), + otherwise pointer to the first character after the value. + The function performs work in place. */ +static char *unquote (char *qstr) +{ + char *endvalue; + int escaped = 0; + char *outptr; + + if(!qstr) return NULL; + + if (qstr[0] == '"') { + qstr++; + outptr = qstr; + + for (endvalue = qstr; endvalue[0] != '\0'; endvalue++, outptr++) { + if (escaped) { + outptr[0] = endvalue[0]; + escaped = 0; + } + else if (endvalue[0] == '\\') { + escaped = 1; + outptr--; /* Will be incremented at the end of the loop */ + } + else if (endvalue[0] == '"') { + break; + } + else { + outptr[0] = endvalue[0]; + } + } + + if (endvalue[0] != '"') { + return NULL; + } + + while (outptr <= endvalue) { + outptr[0] = '\0'; + outptr++; + } + endvalue++; + } + else { /* not qouted value (token) */ + /* qstr already contains output */ + endvalue = skip_token(qstr,0); + }; + + return endvalue; +} + +/* Unlike unquote, this function returns an allocated quoted copy */ +static char *quote (char *str) +{ + char *p; + char *outp; + char *result; + int num_to_escape; /* How many characters need escaping */ + + if (!str) return NULL; + + num_to_escape = 0; + p = strpbrk (str, NEED_ESCAPING); + while (p != NULL) { + num_to_escape++; + p = strpbrk (p + 1, NEED_ESCAPING); + } + + if (num_to_escape == 0) { + return (strdup (str)); + } + + result = malloc (strlen(str) + num_to_escape + 1); + if (result == NULL) { + return NULL; + } + for (p = str, outp = result; *p; p++) { + if (*p == '"' || *p == '\\') { + *outp = '\\'; + outp++; + } + *outp = *p; + outp++; + } + + *outp = '\0'; + + return (result); +} + +static void get_pair(char **in, char **name, char **value) +{ + char *endpair; + char *curp = *in; + *name = NULL; + *value = NULL; + + if (curp == NULL) return; + + while (curp[0] != '\0') { + /* skipping spaces: */ + curp = skip_lws(curp); + + /* 'LWS "," LWS "," ...' is allowed by the DIGEST-MD5 ABNF */ + if (curp[0] == ',') { + curp++; + } else { + break; + } + } + + if (curp[0] == '\0') { + /* End of the string is not an error */ + *name = ""; + return; + } + + *name = curp; + + curp = skip_token(curp,1); + + /* strip wierd chars */ + if (curp[0] != '=' && curp[0] != '\0') { + *curp++ = '\0'; + }; + + curp = skip_lws(curp); + + if (curp[0] != '=') { /* No '=' sign */ + *name = NULL; + return; + } + + curp[0] = '\0'; + curp++; + + curp = skip_lws(curp); + + *value = (curp[0] == '"') ? curp+1 : curp; + + endpair = unquote (curp); + if (endpair == NULL) { /* Unbalanced quotes */ + *name = NULL; + *value = NULL; + return; + } + + /* An optional LWS is allowed after the value. Skip it. */ + if (is_lws_char (endpair[0])) { + /* Remove the trailing LWS from the value */ + *endpair++ = '\0'; + endpair = skip_lws(endpair); + } + + /* syntax check: MUST be '\0' or ',' */ + if (endpair[0] == ',') { + endpair[0] = '\0'; + endpair++; /* skipping <,> */ + } else if (endpair[0] != '\0') { + *name = NULL; + *value = NULL; + return; + } + + *in = endpair; +} + +#ifdef WITH_DES +struct des_context_s { + des_key_schedule keysched; /* key schedule for des initialization */ + des_cblock ivec; /* initial vector for encoding */ + des_key_schedule keysched2; /* key schedule for 3des initialization */ +}; + +typedef struct des_context_s des_context_t; + +/* slide the first 7 bytes of 'inbuf' into the high seven bits of the + first 8 bytes of 'keybuf'. 'keybuf' better be 8 bytes long or longer. */ +static void slidebits(unsigned char *keybuf, unsigned char *inbuf) +{ + keybuf[0] = inbuf[0]; + keybuf[1] = (inbuf[0]<<7) | (inbuf[1]>>1); + keybuf[2] = (inbuf[1]<<6) | (inbuf[2]>>2); + keybuf[3] = (inbuf[2]<<5) | (inbuf[3]>>3); + keybuf[4] = (inbuf[3]<<4) | (inbuf[4]>>4); + keybuf[5] = (inbuf[4]<<3) | (inbuf[5]>>5); + keybuf[6] = (inbuf[5]<<2) | (inbuf[6]>>6); + keybuf[7] = (inbuf[6]<<1); +} + +/****************************** + * + * 3DES functions + * + *****************************/ + +static int dec_3des(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16] __attribute__((unused)), + char *output, + unsigned *outputlen) +{ + des_context_t *c = (des_context_t *) text->cipher_dec_context; + int padding, p; + + des_ede2_cbc_encrypt((void *) input, + (void *) output, + inputlen, + c->keysched, + c->keysched2, + &c->ivec, + DES_DECRYPT); + + /* now chop off the padding */ + padding = output[inputlen - 11]; + if (padding < 1 || padding > 8) { + /* invalid padding length */ + return SASL_FAIL; + } + /* verify all padding is correct */ + for (p = 1; p <= padding; p++) { + if (output[inputlen - 10 - p] != padding) { + return SASL_FAIL; + } + } + + /* chop off the padding */ + *outputlen = inputlen - padding - 10; + + return SASL_OK; +} + +static int enc_3des(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + des_context_t *c = (des_context_t *) text->cipher_enc_context; + int len; + int paddinglen; + + /* determine padding length */ + paddinglen = 8 - ((inputlen + 10) % 8); + + /* now construct the full stuff to be ciphered */ + memcpy(output, input, inputlen); /* text */ + memset(output+inputlen, paddinglen, paddinglen);/* pad */ + memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */ + + len=inputlen+paddinglen+10; + + des_ede2_cbc_encrypt((void *) output, + (void *) output, + len, + c->keysched, + c->keysched2, + &c->ivec, + DES_ENCRYPT); + + *outputlen=len; + + return SASL_OK; +} + +static int init_3des(context_t *text, + unsigned char enckey[16], + unsigned char deckey[16]) +{ + des_context_t *c; + unsigned char keybuf[8]; + + /* allocate enc & dec context */ + c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t)); + if (c == NULL) return SASL_NOMEM; + + /* setup enc context */ + slidebits(keybuf, enckey); + if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0) + return SASL_FAIL; + + slidebits(keybuf, enckey + 7); + if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0) + return SASL_FAIL; + memcpy(c->ivec, ((char *) enckey) + 8, 8); + + text->cipher_enc_context = (cipher_context_t *) c; + + /* setup dec context */ + c++; + slidebits(keybuf, deckey); + if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0) + return SASL_FAIL; + + slidebits(keybuf, deckey + 7); + if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0) + return SASL_FAIL; + + memcpy(c->ivec, ((char *) deckey) + 8, 8); + + text->cipher_dec_context = (cipher_context_t *) c; + + return SASL_OK; +} + + +/****************************** + * + * DES functions + * + *****************************/ + +static int dec_des(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16] __attribute__((unused)), + char *output, + unsigned *outputlen) +{ + des_context_t *c = (des_context_t *) text->cipher_dec_context; + int p, padding = 0; + + des_cbc_encrypt((void *) input, + (void *) output, + inputlen, + c->keysched, + &c->ivec, + DES_DECRYPT); + + /* Update the ivec (des_cbc_encrypt implementations tend to be broken in + this way) */ + memcpy(c->ivec, input + (inputlen - 8), 8); + + /* now chop off the padding */ + padding = output[inputlen - 11]; + if (padding < 1 || padding > 8) { + /* invalid padding length */ + return SASL_FAIL; + } + /* verify all padding is correct */ + for (p = 1; p <= padding; p++) { + if (output[inputlen - 10 - p] != padding) { + return SASL_FAIL; + } + } + + /* chop off the padding */ + *outputlen = inputlen - padding - 10; + + return SASL_OK; +} + +static int enc_des(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + des_context_t *c = (des_context_t *) text->cipher_enc_context; + int len; + int paddinglen; + + /* determine padding length */ + paddinglen = 8 - ((inputlen+10) % 8); + + /* now construct the full stuff to be ciphered */ + memcpy(output, input, inputlen); /* text */ + memset(output+inputlen, paddinglen, paddinglen);/* pad */ + memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */ + + len = inputlen + paddinglen + 10; + + des_cbc_encrypt((void *) output, + (void *) output, + len, + c->keysched, + &c->ivec, + DES_ENCRYPT); + + /* Update the ivec (des_cbc_encrypt implementations tend to be broken in + this way) */ + memcpy(c->ivec, output + (len - 8), 8); + + *outputlen = len; + + return SASL_OK; +} + +static int init_des(context_t *text, + unsigned char enckey[16], + unsigned char deckey[16]) +{ + des_context_t *c; + unsigned char keybuf[8]; + + /* allocate enc context */ + c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t)); + if (c == NULL) return SASL_NOMEM; + + /* setup enc context */ + slidebits(keybuf, enckey); + des_key_sched((des_cblock *) keybuf, c->keysched); + + memcpy(c->ivec, ((char *) enckey) + 8, 8); + + text->cipher_enc_context = (cipher_context_t *) c; + + /* setup dec context */ + c++; + slidebits(keybuf, deckey); + des_key_sched((des_cblock *) keybuf, c->keysched); + + memcpy(c->ivec, ((char *) deckey) + 8, 8); + + text->cipher_dec_context = (cipher_context_t *) c; + + return SASL_OK; +} + +static void free_des(context_t *text) +{ + /* free des contextss. only cipher_enc_context needs to be free'd, + since cipher_dec_context was allocated at the same time. */ + if (text->cipher_enc_context) text->utils->free(text->cipher_enc_context); +} + +#endif /* WITH_DES */ + +#ifdef WITH_RC4 +#ifdef HAVE_OPENSSL +#include <openssl/evp.h> + +static void free_rc4(context_t *text) +{ + if (text->cipher_enc_context) { + EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *)text->cipher_enc_context); + text->cipher_enc_context = NULL; + } + if (text->cipher_dec_context) { + EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *)text->cipher_dec_context); + text->cipher_dec_context = NULL; + } +} + +static int init_rc4(context_t *text, + unsigned char enckey[16], + unsigned char deckey[16]) +{ + EVP_CIPHER_CTX *ctx; + int rc; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) return SASL_NOMEM; + + rc = EVP_EncryptInit_ex(ctx, EVP_rc4(), NULL, enckey, NULL); + if (rc != 1) return SASL_FAIL; + + text->cipher_enc_context = (void *)ctx; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) return SASL_NOMEM; + + rc = EVP_DecryptInit_ex(ctx, EVP_rc4(), NULL, deckey, NULL); + if (rc != 1) return SASL_FAIL; + + text->cipher_dec_context = (void *)ctx; + + return SASL_OK; +} + +static int dec_rc4(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16] __attribute__((unused)), + char *output, + unsigned *outputlen) +{ + int len; + int rc; + + /* decrypt the text part & HMAC */ + rc = EVP_DecryptUpdate((EVP_CIPHER_CTX *)text->cipher_dec_context, + (unsigned char *)output, &len, + (const unsigned char *)input, inputlen); + if (rc != 1) return SASL_FAIL; + + *outputlen = len; + + rc = EVP_DecryptFinal_ex((EVP_CIPHER_CTX *)text->cipher_dec_context, + (unsigned char *)output + len, &len); + if (rc != 1) return SASL_FAIL; + + *outputlen += len; + + /* subtract the HMAC to get the text length */ + *outputlen -= 10; + + return SASL_OK; +} + +static int enc_rc4(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + int len; + int rc; + /* encrypt the text part */ + rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->cipher_enc_context, + (unsigned char *)output, &len, + (const unsigned char *)input, inputlen); + if (rc != 1) return SASL_FAIL; + + *outputlen = len; + + /* encrypt the `MAC part */ + rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->cipher_enc_context, + (unsigned char *)output + *outputlen, &len, + digest, 10); + if (rc != 1) return SASL_FAIL; + + *outputlen += len; + + rc = EVP_EncryptFinal_ex((EVP_CIPHER_CTX *)text->cipher_enc_context, + (unsigned char *)output + *outputlen, &len); + if (rc != 1) return SASL_FAIL; + + *outputlen += len; + + return SASL_OK; +} +#else +/* quick generic implementation of RC4 */ +struct rc4_context_s { + unsigned char sbox[256]; + int i, j; +}; + +typedef struct rc4_context_s rc4_context_t; + +static void rc4_init(rc4_context_t *text, + const unsigned char *key, + unsigned keylen) +{ + int i, j; + + /* fill in linearly s0=0 s1=1... */ + for (i=0;i<256;i++) + text->sbox[i]=i; + + j=0; + for (i = 0; i < 256; i++) { + unsigned char tmp; + /* j = (j + Si + Ki) mod 256 */ + j = (j + text->sbox[i] + key[i % keylen]) % 256; + + /* swap Si and Sj */ + tmp = text->sbox[i]; + text->sbox[i] = text->sbox[j]; + text->sbox[j] = tmp; + } + + /* counters initialized to 0 */ + text->i = 0; + text->j = 0; +} + +static void rc4_encrypt(rc4_context_t *text, + const char *input, + char *output, + unsigned len) +{ + int tmp; + int i = text->i; + int j = text->j; + int t; + int K; + const char *input_end = input + len; + + while (input < input_end) { + i = (i + 1) % 256; + + j = (j + text->sbox[i]) % 256; + + /* swap Si and Sj */ + tmp = text->sbox[i]; + text->sbox[i] = text->sbox[j]; + text->sbox[j] = tmp; + + t = (text->sbox[i] + text->sbox[j]) % 256; + + K = text->sbox[t]; + + /* byte K is Xor'ed with plaintext */ + *output++ = *input++ ^ K; + } + + text->i = i; + text->j = j; +} + +static void rc4_decrypt(rc4_context_t *text, + const char *input, + char *output, + unsigned len) +{ + int tmp; + int i = text->i; + int j = text->j; + int t; + int K; + const char *input_end = input + len; + + while (input < input_end) { + i = (i + 1) % 256; + + j = (j + text->sbox[i]) % 256; + + /* swap Si and Sj */ + tmp = text->sbox[i]; + text->sbox[i] = text->sbox[j]; + text->sbox[j] = tmp; + + t = (text->sbox[i] + text->sbox[j]) % 256; + + K = text->sbox[t]; + + /* byte K is Xor'ed with plaintext */ + *output++ = *input++ ^ K; + } + + text->i = i; + text->j = j; +} + +static void free_rc4(context_t *text) +{ + /* free rc4 context structures */ + + if (text->cipher_enc_context) { + text->utils->free(text->cipher_enc_context); + text->cipher_enc_context = NULL; + } + if (text->cipher_dec_context) { + text->utils->free(text->cipher_dec_context); + text->cipher_dec_context = NULL; + } +} + +static int init_rc4(context_t *text, + unsigned char enckey[16], + unsigned char deckey[16]) +{ + /* allocate rc4 context structures */ + text->cipher_enc_context= + (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t)); + if (text->cipher_enc_context == NULL) return SASL_NOMEM; + + text->cipher_dec_context= + (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t)); + if (text->cipher_dec_context == NULL) return SASL_NOMEM; + + /* initialize them */ + rc4_init((rc4_context_t *) text->cipher_enc_context, + (const unsigned char *) enckey, 16); + rc4_init((rc4_context_t *) text->cipher_dec_context, + (const unsigned char *) deckey, 16); + + return SASL_OK; +} + +static int dec_rc4(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16] __attribute__((unused)), + char *output, + unsigned *outputlen) +{ + /* decrypt the text part & HMAC */ + rc4_decrypt((rc4_context_t *) text->cipher_dec_context, + input, output, inputlen); + + /* no padding so we just subtract the HMAC to get the text length */ + *outputlen = inputlen - 10; + + return SASL_OK; +} + +static int enc_rc4(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + /* pad is zero */ + *outputlen = inputlen+10; + + /* encrypt the text part */ + rc4_encrypt((rc4_context_t *) text->cipher_enc_context, + input, + output, + inputlen); + + /* encrypt the HMAC part */ + rc4_encrypt((rc4_context_t *) text->cipher_enc_context, + (const char *) digest, + (output)+inputlen, 10); + + return SASL_OK; +} +#endif /* HAVE_OPENSSL */ +#endif /* WITH_RC4 */ + +struct digest_cipher available_ciphers[] = +{ +#ifdef WITH_RC4 + { "rc4-40", 40, 5, 0x01, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, + { "rc4-56", 56, 7, 0x02, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, + { "rc4", 128, 16, 0x04, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, +#endif +#ifdef WITH_DES + { "des", 55, 16, 0x08, &enc_des, &dec_des, &init_des, &free_des }, + { "3des", 112, 16, 0x10, &enc_3des, &dec_3des, &init_3des, &free_des }, +#endif + { NULL, 0, 0, 0, NULL, NULL, NULL, NULL } +}; + +static int create_layer_keys(context_t *text, + const sasl_utils_t *utils, + HASH key, int keylen, + unsigned char enckey[16], + unsigned char deckey[16]) +{ + MD5_CTX Md5Ctx; + + utils->log(utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 create_layer_keys()"); + + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, key, keylen); + if (text->i_am == SERVER) { + utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_SERVER_CLIENT, + (unsigned) strlen(SEALING_SERVER_CLIENT)); + } else { + utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_CLIENT_SERVER, + (unsigned) strlen(SEALING_CLIENT_SERVER)); + } + utils->MD5Final(enckey, &Md5Ctx); + + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, key, keylen); + if (text->i_am != SERVER) { + utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_SERVER_CLIENT, + (unsigned) strlen(SEALING_SERVER_CLIENT)); + } else { + utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_CLIENT_SERVER, + (unsigned) strlen(SEALING_CLIENT_SERVER)); + } + utils->MD5Final(deckey, &Md5Ctx); + + /* create integrity keys */ + /* sending */ + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN); + if (text->i_am == SERVER) { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT, + (unsigned) strlen(SIGNING_SERVER_CLIENT)); + } else { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER, + (unsigned) strlen(SIGNING_CLIENT_SERVER)); + } + utils->MD5Final(text->Ki_send, &Md5Ctx); + + /* receiving */ + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN); + if (text->i_am != SERVER) { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT, + (unsigned) strlen(SIGNING_SERVER_CLIENT)); + } else { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER, + (unsigned) strlen(SIGNING_CLIENT_SERVER)); + } + utils->MD5Final(text->Ki_receive, &Md5Ctx); + + return SASL_OK; +} + +static const unsigned short version = 1; + +/* + * privacy: + * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum + * + * integrity: + * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum + */ +static int digestmd5_encode(void *context, + const struct iovec *invec, + unsigned numiov, + const char **output, + unsigned *outputlen) +{ + context_t *text = (context_t *) context; + int tmp; + unsigned int tmpnum; + unsigned short int tmpshort; + int ret; + char *out; + struct buffer_info *inblob, bufinfo; + + if(!context || !invec || !numiov || !output || !outputlen) { + if (text) PARAMERROR(text->utils); + return SASL_BADPARAM; + } + + if (numiov > 1) { + ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf); + if (ret != SASL_OK) return ret; + inblob = text->enc_in_buf; + } else { + /* avoid the data copy */ + bufinfo.data = invec[0].iov_base; + bufinfo.curlen = invec[0].iov_len; + inblob = &bufinfo; + } + + /* make sure the output buffer is big enough for this blob */ + ret = _plug_buf_alloc(text->utils, &(text->encode_buf), + &(text->encode_buf_len), + (4 + /* for length */ + inblob->curlen + /* for content */ + 10 + /* for MAC */ + 8 + /* maximum pad */ + 6)); /* for ver and seqnum */ + if(ret != SASL_OK) return ret; + + /* skip by the length for now */ + out = (text->encode_buf)+4; + + /* construct (seqnum, msg) + * + * Use the output buffer so that the message text is already in place + * for an integrity-only layer. + */ + tmpnum = htonl(text->seqnum); + memcpy(text->encode_buf, &tmpnum, 4); + memcpy(text->encode_buf + 4, inblob->data, inblob->curlen); + + if (text->cipher_enc) { + unsigned char digest[16]; + + /* HMAC(ki, (seqnum, msg) ) */ + text->utils->hmac_md5((const unsigned char *) text->encode_buf, + inblob->curlen + 4, + text->Ki_send, HASHLEN, digest); + + /* calculate the encrypted part */ + text->cipher_enc(text, inblob->data, inblob->curlen, + digest, out, outputlen); + out+=(*outputlen); + } + else { + /* HMAC(ki, (seqnum, msg) ) -- put directly into output buffer */ + text->utils->hmac_md5((const unsigned char *) text->encode_buf, + inblob->curlen + 4, + text->Ki_send, HASHLEN, + (unsigned char *) text->encode_buf + + inblob->curlen + 4); + + *outputlen = inblob->curlen + 10; /* for message + CMAC */ + out+=inblob->curlen + 10; + } + + /* copy in version */ + tmpshort = htons(version); + memcpy(out, &tmpshort, 2); /* 2 bytes = version */ + + out+=2; + (*outputlen)+=2; /* for version */ + + /* put in seqnum */ + tmpnum = htonl(text->seqnum); + memcpy(out, &tmpnum, 4); /* 4 bytes = seq # */ + + (*outputlen)+=4; /* for seqnum */ + + /* put the 1st 4 bytes in */ + tmp=htonl(*outputlen); + memcpy(text->encode_buf, &tmp, 4); + + (*outputlen)+=4; + + *output = text->encode_buf; + text->seqnum++; + + return SASL_OK; +} + +static int digestmd5_decode_packet(void *context, + const char *input, + unsigned inputlen, + char **output, + unsigned *outputlen) +{ + context_t *text = (context_t *) context; + int result; + unsigned char *digest; + int tmpnum; + int lup; + unsigned short ver; + unsigned int seqnum; + unsigned char checkdigest[16]; + + if (inputlen < 16) { + text->utils->seterror(text->utils->conn, 0, "DIGEST-MD5 SASL packets must be at least 16 bytes long"); + return SASL_FAIL; + } + + /* check the version number */ + memcpy(&ver, input+inputlen-6, 2); + ver = ntohs(ver); + if (ver != version) { + text->utils->seterror(text->utils->conn, 0, "Wrong Version"); + return SASL_FAIL; + } + + /* check the sequence number */ + memcpy(&seqnum, input+inputlen-4, 4); + seqnum = ntohl(seqnum); + + if (seqnum != text->rec_seqnum) { + text->utils->seterror(text->utils->conn, 0, + "Incorrect Sequence Number: received %u, expected %u", + seqnum, + text->rec_seqnum); + return SASL_FAIL; + } + + /* allocate a buffer large enough for the output */ + result = _plug_buf_alloc(text->utils, &text->decode_packet_buf, + &text->decode_packet_buf_len, + inputlen /* length of message */ + - 6 /* skip ver and seqnum */ + + 4); /* prepend seqnum */ + if (result != SASL_OK) return result; + + /* construct (seqnum, msg) */ + tmpnum = htonl(text->rec_seqnum); + memcpy(text->decode_packet_buf, &tmpnum, 4); + + text->rec_seqnum++; /* now increment it */ + + *output = text->decode_packet_buf + 4; /* skip seqnum */ + + if (text->cipher_dec) { + /* decrypt message & HMAC into output buffer */ + result = text->cipher_dec(text, input, inputlen-6, NULL, + *output, outputlen); + if (result != SASL_OK) return result; + } + else { + /* copy message & HMAC into output buffer */ + memcpy(*output, input, inputlen - 6); + *outputlen = inputlen - 16; /* -16 to skip HMAC, ver and seqnum */ + } + digest = (unsigned char *) *output + (inputlen - 16); + + /* check the CMAC */ + + /* HMAC(ki, (seqnum, msg) ) */ + text->utils->hmac_md5((const unsigned char *) text->decode_packet_buf, + (*outputlen) + 4, + text->Ki_receive, HASHLEN, checkdigest); + + /* now check it */ + for (lup = 0; lup < 10; lup++) + if (checkdigest[lup] != digest[lup]) { + text->utils->seterror(text->utils->conn, 0, + "CMAC doesn't match at byte %d!", lup); + return SASL_FAIL; + } + + return SASL_OK; +} + +static int digestmd5_decode(void *context, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen) +{ + context_t *text = (context_t *) context; + int ret; + + ret = _plug_decode(&text->decode_context, input, inputlen, + &text->decode_buf, &text->decode_buf_len, outputlen, + digestmd5_decode_packet, text); + + *output = text->decode_buf; + + return ret; +} + +static void digestmd5_common_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + context_t *text = (context_t *) conn_context; + int lup; + + if (!text || !utils) return; + + utils->log(utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 common mech dispose"); + + if (text->authid) utils->free(text->authid); + if (text->realm) utils->free(text->realm); + + if (text->realms) { + /* need to free all the realms */ + for (lup = 0; lup < text->realm_cnt; lup++) + utils->free (text->realms[lup]); + + utils->free(text->realms); + } + + if (text->nonce) utils->free(text->nonce); + if (text->cnonce) utils->free(text->cnonce); + + if (text->cipher_free) text->cipher_free(text); + + /* free the stuff in the context */ + if (text->response_value) utils->free(text->response_value); + + _plug_decode_free(&text->decode_context); + if (text->encode_buf) utils->free(text->encode_buf); + if (text->decode_buf) utils->free(text->decode_buf); + if (text->decode_packet_buf) utils->free(text->decode_packet_buf); + if (text->out_buf) utils->free(text->out_buf); + + if (text->enc_in_buf) { + if (text->enc_in_buf->data) utils->free(text->enc_in_buf->data); + utils->free(text->enc_in_buf); + } + + utils->free(conn_context); +} + +static void clear_reauth_entry(reauth_entry_t *reauth, enum Context_type type, + const sasl_utils_t *utils) +{ + if (!reauth) return; + + if (reauth->authid) utils->free(reauth->authid); + if (reauth->realm) utils->free(reauth->realm); + if (reauth->nonce) utils->free(reauth->nonce); + if (reauth->cnonce) utils->free(reauth->cnonce); + + if (type == CLIENT) { + if (reauth->u.c.serverFQDN) utils->free(reauth->u.c.serverFQDN); + } + + memset(reauth, 0, sizeof(reauth_entry_t)); +} + +static void digestmd5_common_mech_free(void *glob_context, + const sasl_utils_t *utils) +{ + digest_glob_context_t *my_glob_context = + (digest_glob_context_t *) glob_context; + reauth_cache_t *reauth_cache = my_glob_context->reauth; + size_t n; + + utils->log(utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 common mech free"); + + /* Prevent anybody else from freeing this as well */ + my_glob_context->reauth = NULL; + + if (!reauth_cache) return; + + for (n = 0; n < reauth_cache->size; n++) { + clear_reauth_entry(&reauth_cache->e[n], reauth_cache->i_am, utils); + } + if (reauth_cache->e) utils->free(reauth_cache->e); + + if (reauth_cache->mutex) { + utils->mutex_free(reauth_cache->mutex); + reauth_cache->mutex = NULL; + } + + utils->free(reauth_cache); +} + +/***************************** Server Section *****************************/ + +typedef struct server_context { + context_t common; + + time_t timestamp; + int stale; /* last nonce is stale */ + sasl_ssf_t limitssf, requiressf; /* application defined bounds */ +} server_context_t; + +static digest_glob_context_t server_glob_context; + +static void DigestCalcHA1FromSecret(context_t * text, + const sasl_utils_t * utils, + HASH HA1, + unsigned char *authorization_id, + unsigned char *pszNonce, + unsigned char *pszCNonce, + HASHHEX SessionKey) +{ + MD5_CTX Md5Ctx; + + /* calculate session key */ + utils->MD5Init(&Md5Ctx); + if (text->http_mode) { + /* per RFC 2617 Errata ID 1649 */ + HASHHEX HA1Hex; + + CvtHex(HA1, HA1Hex); + utils->MD5Update(&Md5Ctx, HA1Hex, HASHHEXLEN); + } + else { + /* per RFC 2831 */ + utils->MD5Update(&Md5Ctx, HA1, HASHLEN); + } + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce)); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce)); + if (authorization_id != NULL) { + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, authorization_id, + (unsigned) strlen((char *) authorization_id)); + } + utils->MD5Final(HA1, &Md5Ctx); + + CvtHex(HA1, SessionKey); + + + /* save HA1 because we need it to make the privacy and integrity keys */ + memcpy(text->HA1, HA1, sizeof(HASH)); +} + +static char *create_response(context_t * text, + const sasl_utils_t * utils, + unsigned char *nonce, + unsigned int ncvalue, + unsigned char *cnonce, + char *qop, + const sasl_http_request_t *request, + HASH Secret, + char *authorization_id, + char **response_value) +{ + HASHHEX SessionKey; + HASH EntityHash; + HASHHEX HEntity; + HASHHEX Response; + char *result; + + if (qop == NULL) qop = "auth"; + + DigestCalcHA1FromSecret(text, + utils, + Secret, + (unsigned char *) authorization_id, + nonce, + cnonce, + SessionKey); + + if (text->http_mode) { + /* per RFC 2617 */ + MD5_CTX Md5Ctx; + + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, request->entity, request->elen); + utils->MD5Final(EntityHash, &Md5Ctx); + } + else { + /* per RFC 2831 */ + memset(EntityHash, 0, HASHLEN); + } + CvtHex(EntityHash, HEntity); + + /* Calculate response for comparison with client's response */ + DigestCalcResponse(utils, + SessionKey,/* HEX(H(A1)) */ + nonce, /* nonce from server */ + ncvalue, /* 8 hex digits */ + cnonce, /* client nonce */ + (unsigned char *) qop, /* qop-value: "", "auth", + * "auth-int" */ + (unsigned char *) request->uri, /* requested URL */ + (unsigned char *) request->method, + HEntity, /* H(entity body) if qop="auth-int" */ + Response /* request-digest or response-digest */ + ); + + result = utils->malloc(HASHHEXLEN + 1); + memcpy(result, Response, HASHHEXLEN); + result[HASHHEXLEN] = 0; + + /* Calculate response value for mutual auth with the client (NO Method) */ + if (response_value != NULL) { + char * new_response_value; + + DigestCalcResponse(utils, + SessionKey, /* HEX(H(A1)) */ + nonce, /* nonce from server */ + ncvalue, /* 8 hex digits */ + cnonce, /* client nonce */ + (unsigned char *) qop, /* qop-value: "", "auth", + * "auth-int" */ + (unsigned char *) request->uri, /* requested URL */ + NULL, + HEntity, /* H(entity body) if qop="auth-int" */ + Response /* request-digest or response-digest */ + ); + + new_response_value = utils->realloc(*response_value, HASHHEXLEN + 1); + if (new_response_value == NULL) { + free (*response_value); + *response_value = NULL; + return NULL; + } + *response_value = new_response_value; + + memcpy(*response_value, Response, HASHHEXLEN); + (*response_value)[HASHHEXLEN] = 0; + } + return result; +} + +static int get_server_realm(sasl_server_params_t * params, char **realm) +{ + /* look at user realm first */ + if (params->user_realm != NULL) { + if(params->user_realm[0] != '\0') { + *realm = (char *) params->user_realm; + } else { + /* Catch improperly converted apps */ + params->utils->seterror(params->utils->conn, 0, + "user_realm is an empty string!"); + return SASL_BADPARAM; + } + } else if (params->serverFQDN != NULL) { + *realm = (char *) params->serverFQDN; + } else { + params->utils->seterror(params->utils->conn, 0, + "no way to obtain DIGEST-MD5 realm"); + return SASL_FAIL; + } + + return SASL_OK; +} + +/* + * Convert hex string to int + */ +static int htoi(unsigned char *hexin, unsigned int *res) +{ + size_t lup, inlen; + inlen = strlen((char *) hexin); + + *res = 0; + for (lup = 0; lup < inlen; lup++) { + switch (hexin[lup]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *res = (*res << 4) + (hexin[lup] - '0'); + break; + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + *res = (*res << 4) + (hexin[lup] - 'a' + 10); + break; + + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + *res = (*res << 4) + (hexin[lup] - 'A' + 10); + break; + + default: + return SASL_BADPARAM; + } + + } + + return SASL_OK; +} + +static int digestmd5_server_mech_new(void *glob_context, + sasl_server_params_t * sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + context_t *text; + + /* holds state are in -- allocate server size */ + text = sparams->utils->malloc(sizeof(server_context_t)); + if (text == NULL) + return SASL_NOMEM; + memset((server_context_t *)text, 0, sizeof(server_context_t)); + + text->state = 1; + text->i_am = SERVER; + text->http_mode = (sparams->flags & SASL_NEED_HTTP); + text->reauth = ((digest_glob_context_t *) glob_context)->reauth; + + *conn_context = text; + return SASL_OK; +} + +static int +digestmd5_server_mech_step1(server_context_t *stext, + sasl_server_params_t *sparams, + const char *clientin __attribute__((unused)), + unsigned clientinlen __attribute__((unused)), + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t * oparams __attribute__((unused))) +{ + context_t *text = (context_t *) stext; + int result; + char *realm; + unsigned char *nonce; + char *charset = "utf-8"; + char qop[1024], cipheropts[1024]; + struct digest_cipher *cipher; + unsigned resplen; + int added_conf = 0; + char maxbufstr[64]; + + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 server step 1"); + + /* get realm */ + result = get_server_realm(sparams, &realm); + if(result != SASL_OK) return result; + + /* what options should we offer the client? */ + qop[0] = '\0'; + cipheropts[0] = '\0'; + if (stext->requiressf == 0) { + if (*qop) strcat(qop, ","); + strcat(qop, "auth"); + } + if (stext->requiressf <= 1 && stext->limitssf >= 1) { + if (*qop) strcat(qop, ","); + strcat(qop, "auth-int"); + } + + cipher = available_ciphers; + while (cipher->name) { + /* do we allow this particular cipher? */ + if (stext->requiressf <= cipher->ssf && + stext->limitssf >= cipher->ssf) { + if (!added_conf) { + if (*qop) strcat(qop, ","); + strcat(qop, "auth-conf"); + added_conf = 1; + } + if (strlen(cipheropts) + strlen(cipher->name) + 1 >= 1024) + return SASL_FAIL; + if (*cipheropts) strcat(cipheropts, ","); + strcat(cipheropts, cipher->name); + } + cipher++; + } + + if (*qop == '\0') { + /* we didn't allow anything?!? we'll return SASL_TOOWEAK, since + that's close enough */ + return SASL_TOOWEAK; + } + + /* + * digest-challenge = 1#( realm | nonce | qop-options | stale | maxbuf | + * charset | cipher-opts | auth-param ) + */ + + nonce = create_nonce(sparams->utils); + if (nonce == NULL) { + SETERROR(sparams->utils, "internal erorr: failed creating a nonce"); + return SASL_FAIL; + } + + resplen = 0; + text->out_buf = NULL; + text->out_buf_len = 0; + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "nonce", (unsigned char *) nonce, + TRUE) != SASL_OK) { + SETERROR(sparams->utils, "internal error: add_to_challenge failed"); + return SASL_FAIL; + } + + /* add to challenge; if we chose not to specify a realm, we won't + * send one to the client */ + if (realm && add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "realm", (unsigned char *) realm, + TRUE) != SASL_OK) { + SETERROR(sparams->utils, "internal error: add_to_challenge failed"); + return SASL_FAIL; + } + /* + * qop-options A quoted string of one or more tokens indicating the + * "quality of protection" values supported by the server. The value + * "auth" indicates authentication; the value "auth-int" indicates + * authentication with integrity protection; the value "auth-conf" + * indicates authentication with integrity protection and encryption. + */ + + /* add qop to challenge */ + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "qop", + (unsigned char *) qop, TRUE) != SASL_OK) { + SETERROR(sparams->utils, "internal error: add_to_challenge 3 failed"); + return SASL_FAIL; + } + + /* + * Cipheropts - list of ciphers server supports + */ + /* add cipher-opts to challenge; only add if there are some */ + if (strcmp(cipheropts,"")!=0) + { + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "cipher", (unsigned char *) cipheropts, + TRUE) != SASL_OK) { + SETERROR(sparams->utils, + "internal error: add_to_challenge 4 failed"); + return SASL_FAIL; + } + } + + /* "stale" is true if a reauth failed because of a nonce timeout */ + if (stext->stale && + add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "stale", (unsigned char *) "true", FALSE) != SASL_OK) { + SETERROR(sparams->utils, "internal error: add_to_challenge failed"); + return SASL_FAIL; + } + + /* + * maxbuf A number indicating the size of the largest buffer the server + * is able to receive when using "auth-int". If this directive is + * missing, the default value is 65536. This directive may appear at most + * once; if multiple instances are present, the client should abort the + * authentication exchange. + */ + if(sparams->props.maxbufsize) { + snprintf(maxbufstr, sizeof(maxbufstr), "%u", + sparams->props.maxbufsize); + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "maxbuf", + (unsigned char *) maxbufstr, FALSE) != SASL_OK) { + SETERROR(sparams->utils, + "internal error: add_to_challenge 5 failed"); + return SASL_FAIL; + } + } + + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "charset", + (unsigned char *) charset, FALSE) != SASL_OK) { + SETERROR(sparams->utils, "internal error: add_to_challenge 6 failed"); + return SASL_FAIL; + } + + + /* + * algorithm + * This directive is required for backwards compatibility with HTTP + * Digest, which supports other algorithms. This directive is + * required and MUST appear exactly once; if not present, or if multiple + * instances are present, the client should abort the authentication + * exchange. + * + * algorithm = "algorithm" "=" "md5-sess" + */ + + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "algorithm", + (unsigned char *) "md5-sess", FALSE)!=SASL_OK) { + SETERROR(sparams->utils, "internal error: add_to_challenge 7 failed"); + return SASL_FAIL; + } + + /* + * The size of a digest-challenge MUST be less than 2048 bytes!!! + */ + if (*serveroutlen > 2048) { + SETERROR(sparams->utils, + "internal error: challenge larger than 2048 bytes"); + return SASL_FAIL; + } + + text->authid = NULL; + if (_plug_strdup(sparams->utils, realm, &text->realm, NULL) != SASL_OK) { + SETERROR(sparams->utils, + "internal error: out of memory when saving realm"); + return SASL_FAIL; + } + + if (text->http_mode && text->reauth->timeout && + sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + + /* Create an initial cache entry for non-persistent HTTP connections */ + unsigned val = hash((char *) nonce) % text->reauth->size; + + clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); + text->reauth->e[val].authid = NULL; + text->reauth->e[val].realm = text->realm; text->realm = NULL; + text->reauth->e[val].nonce = nonce; + text->reauth->e[val].nonce_count = 1; + text->reauth->e[val].cnonce = NULL; + text->reauth->e[val].u.s.timestamp = time(0); + + sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + else { + text->nonce = nonce; + text->nonce_count = 1; + text->cnonce = NULL; + stext->timestamp = time(0); + } + + *serveroutlen = (unsigned) strlen(text->out_buf); + *serverout = text->out_buf; + + text->state = 2; + + return SASL_CONTINUE; +} + +static int digestmd5_server_mech_step2(server_context_t *stext, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t * oparams) +{ + context_t *text = (context_t *) stext; + /* verify digest */ + sasl_secret_t *sec = NULL; + int result; + char *serverresponse = NULL; + char *username = NULL; + char *authorization_id = NULL; + char *realm = NULL; + unsigned char *nonce = NULL, *cnonce = NULL; + unsigned int noncecount = 0; + char *qop = NULL; + char *digesturi = NULL; + sasl_http_request_t rfc2831_request; + const sasl_http_request_t *request; + char *response = NULL; + + /* setting the default value (65536) */ + unsigned long client_maxbuf = 65536; + int maxbuf_count = 0; /* How many maxbuf instances was found */ + + char *charset = NULL; + char *cipher = NULL; + unsigned int n = 0; + + HASH Secret; + HASH SecretBogus; + bool Try_8859_1 = FALSE; + int client_ignores_realm = 0; + char *full_username = NULL; + char *internal_username = NULL; + int canon_flags; + + /* password prop_request */ + const char *password_request[] = { SASL_AUX_PASSWORD, +#if defined(OBSOLETE_DIGEST_ATTR) + "*cmusaslsecretDIGEST-MD5", +#endif + NULL }; + size_t len; + struct propval auxprop_values[2]; + + /* can we mess with clientin? copy it to be safe */ + char *in_start = NULL; + char *in = NULL; + cipher_free_t *old_cipher_free = NULL; + + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 server step 2"); + + if (clientinlen == 0) { + SETERROR(sparams->utils, "input expected in DIGEST-MD5, step 2"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + + if (text->http_mode) { + /* per RFC 2617 (HTTP Request as set by calling application) */ + request = sparams->http_request; + if (!request) { + SETERROR(sparams->utils, + "missing HTTP request in DIGEST-MD5, step 2"); + result = SASL_BADPARAM; + goto FreeAllMem; + } + } + else { + /* per RFC 2831 */ + rfc2831_request.method = "AUTHENTICATE"; + rfc2831_request.uri = NULL; /* to be filled in below from response */ + rfc2831_request.entity = NULL; + rfc2831_request.elen = 0; + rfc2831_request.non_persist = 0; + request = &rfc2831_request; + } + + in = sparams->utils->malloc(clientinlen + 1); + + memcpy(in, clientin, clientinlen); + in[clientinlen] = 0; + + in_start = in; + + + /* parse what we got */ + while (in[0] != '\0') { + char *name = NULL, *value = NULL; + get_pair(&in, &name, &value); + + if (name == NULL) { + SETERROR(sparams->utils, + "Parse error"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + + if (*name == '\0') { + break; + } + + /* Extracting parameters */ + + /* + * digest-response = 1#( username | realm | nonce | cnonce | + * nonce-count | qop | digest-uri | response | maxbuf | charset | + * cipher | auth-param ) + */ + + if (strcasecmp(name, "username") == 0) { + _plug_strdup(sparams->utils, value, &username, NULL); + } else if (strcasecmp(name, "authzid") == 0) { + _plug_strdup(sparams->utils, value, &authorization_id, NULL); + } else if (strcasecmp(name, "cnonce") == 0) { + _plug_strdup(sparams->utils, value, (char **) &cnonce, NULL); + } else if (strcasecmp(name, "nc") == 0) { + if (htoi((unsigned char *) value, &noncecount) != SASL_OK) { + SETERROR(sparams->utils, + "error converting hex to int"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + } else if (strcasecmp(name, "realm") == 0) { + if (realm) { + SETERROR(sparams->utils, + "duplicate realm: authentication aborted"); + result = SASL_FAIL; + goto FreeAllMem; + } + _plug_strdup(sparams->utils, value, &realm, NULL); + } else if (strcasecmp(name, "nonce") == 0) { + _plug_strdup(sparams->utils, value, (char **) &nonce, NULL); + } else if (strcasecmp(name, "qop") == 0) { + if (qop) { + SETERROR(sparams->utils, + "duplicate qop: authentication aborted"); + result = SASL_FAIL; + goto FreeAllMem; + } + _plug_strdup(sparams->utils, value, &qop, NULL); + } else if (strcasecmp(name, "digest-uri") == 0 || /* per RFC 2831 */ + (text->http_mode && + strcasecmp(name, "uri") == 0)) { /* per RFC 2617 */ + size_t service_len; + + if (digesturi) { + SETERROR(sparams->utils, + "duplicate digest-uri: authentication aborted"); + result = SASL_FAIL; + goto FreeAllMem; + } + + _plug_strdup(sparams->utils, value, &digesturi, NULL); + + if (text->http_mode && request && request->uri) { + /* Verify digest-uri matches HTTP request (per RFC 2617) */ + if (strcmp(digesturi, request->uri)) { + result = SASL_BADAUTH; + SETERROR(sparams->utils, + "bad digest-uri: doesn't match HTTP request"); + goto FreeAllMem; + } + } + else { + /* Verify digest-uri format (per RFC 2831): + * + * digest-uri-value = serv-type "/" host [ "/" serv-name ] + */ + + /* make sure it's the service that we're expecting */ + service_len = strlen(sparams->service); + if (strncasecmp(digesturi, sparams->service, service_len) || + digesturi[service_len] != '/') { + result = SASL_BADAUTH; + SETERROR(sparams->utils, + "bad digest-uri: doesn't match service"); + goto FreeAllMem; + } + + /* xxx we don't verify the hostname component */ + + rfc2831_request.uri = digesturi; + } + + } else if (strcasecmp(name, "response") == 0) { + _plug_strdup(sparams->utils, value, &response, NULL); + } else if (strcasecmp(name, "cipher") == 0) { + _plug_strdup(sparams->utils, value, &cipher, NULL); + } else if (strcasecmp(name, "maxbuf") == 0) { + maxbuf_count++; + if (maxbuf_count != 1) { + result = SASL_BADAUTH; + SETERROR(sparams->utils, + "duplicate maxbuf: authentication aborted"); + goto FreeAllMem; + } else if (str2ul32 (value, &client_maxbuf) == FALSE) { + result = SASL_BADAUTH; + SETERROR(sparams->utils, "invalid maxbuf parameter"); + goto FreeAllMem; + } else { + if (client_maxbuf <= 16) { + result = SASL_BADAUTH; + SETERROR(sparams->utils, + "maxbuf parameter too small"); + goto FreeAllMem; + } + + if (client_maxbuf > MAX_SASL_BUFSIZE) { + result = SASL_BADAUTH; + SETERROR(sparams->utils, + "maxbuf parameter too big"); + goto FreeAllMem; + } + } + } else if (strcasecmp(name, "charset") == 0) { + if (strcasecmp(value, "utf-8") != 0) { + SETERROR(sparams->utils, "client doesn't support UTF-8"); + result = SASL_FAIL; + goto FreeAllMem; + } + _plug_strdup(sparams->utils, value, &charset, NULL); + } else if (strcasecmp(name,"algorithm") == 0) { + /* per RFC 2831: algorithm MUST be ignored if received */ + if (text->http_mode && strcasecmp(value, "md5-sess") != 0) { + /* per RFC 2617: algorithm MUST match that sent in challenge */ + SETERROR(sparams->utils, "'algorithm' isn't 'md5-sess'"); + result = SASL_FAIL; + goto FreeAllMem; + } + } else { + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 unrecognized pair %s/%s: ignoring", + name, value); + } + } + + /* + * username = "username" "=" <"> username-value <"> + * username-value = qdstr-val + * cnonce = "cnonce" "=" <"> cnonce-value <"> + * cnonce-value = qdstr-val + * nonce-count = "nc" "=" nc-value + * nc-value = 8LHEX + * qop = "qop" "=" qop-value + * digest-uri = "digest-uri" "=" digest-uri-value + * digest-uri-value = serv-type "/" host [ "/" serv-name ] + * serv-type = 1*ALPHA + * host = 1*( ALPHA | DIGIT | "-" | "." ) + * service = host + * response = "response" "=" <"> response-value <"> + * response-value = 32LHEX + * LHEX = "0" | "1" | "2" | "3" | "4" | "5" | + * "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f" + * cipher = "cipher" "=" cipher-value + */ + /* Verifing that all required parameters were received */ + if ((username == NULL)) { + SETERROR(sparams->utils, "required parameters missing: username"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + if ((nonce == NULL)) { + SETERROR(sparams->utils, "required parameters missing: nonce"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + if ((noncecount == 0)) { + SETERROR(sparams->utils, "required parameters missing: noncecount"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + if ((cnonce == NULL)) { + SETERROR(sparams->utils, "required parameters missing: cnonce"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + if ((digesturi == NULL)) { + SETERROR(sparams->utils, "required parameters missing: digesturi"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + if ((response == NULL)) { + SETERROR(sparams->utils, "required parameters missing: response"); + result = SASL_BADAUTH; + goto FreeAllMem; + } + + if (realm == NULL) { + /* From 2831bis: + If the directive is missing, "realm-value" will set to + the empty string when computing A1. */ + _plug_strdup(sparams->utils, "", &realm, NULL); + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "The client didn't send a realm, assuming empty string."); +#if 0 + if (text->realm[0] != '\0') { + SETERROR(sparams->utils, + "realm changed: authentication aborted"); + result = SASL_BADAUTH; + goto FreeAllMem; + } +#endif + } + + if (!text->nonce && text->reauth->timeout && text->reauth->size > 0) { + unsigned val = hash((char *) nonce) % text->reauth->size; + + /* reauth attempt or continuation of HTTP Digest on a + non-persistent connection, see if we have any info for this nonce */ + if (sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + if (text->reauth->e[val].realm && + !strcmp(realm, text->reauth->e[val].realm) && + ((text->reauth->e[val].nonce_count == 1) || + (text->reauth->e[val].authid && + !strcmp(username, text->reauth->e[val].authid)))) { + + _plug_strdup(sparams->utils, text->reauth->e[val].realm, + &text->realm, NULL); + _plug_strdup(sparams->utils, (char *) text->reauth->e[val].nonce, + (char **) &text->nonce, NULL); + text->nonce_count = text->reauth->e[val].nonce_count; +#if 0 /* XXX Neither RFC 2617 nor RFC 2831 state that the cnonce + needs to remain constant for subsequent authentication to work */ + _plug_strdup(sparams->utils, (char *) text->reauth->e[val].cnonce, + (char **) &text->cnonce, NULL); +#endif + stext->timestamp = text->reauth->e[val].u.s.timestamp; + } + sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + if (!text->nonce) { + /* we don't have any reauth info */ + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "No reauth info for '%s' found", nonce); + + /* we will continue processing the response to determine + if the client knows the password and return stale accordingly */ + } + } + + /* Sanity check the parameters */ + if (text->nonce) { + /* CLAIM: realm is not NULL below */ + if (text->realm == NULL) { + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "The client specifies a realm when the server hasn't provided one. Using client's realm."); + _plug_strdup(sparams->utils, realm, &text->realm, NULL); + } else if ((strcmp(realm, text->realm) != 0) && + /* XXX - Not sure why the check for text->realm not being empty is needed, + as it should always be non-empty */ + (text->realm[0] != 0)) { + + client_ignores_realm = 1; + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "The client tries to override server provided realm"); + if (text->realm) sparams->utils->free(text->realm); + _plug_strdup(sparams->utils, realm, &text->realm, NULL); + } + + if (strcmp((char *) nonce, (char *) text->nonce) != 0) { + SETERROR(sparams->utils, + "nonce changed: authentication aborted"); + result = SASL_BADAUTH; + goto FreeAllMem; + } +#if 0 /* XXX Possible replay attack, but we will continue processing + * the response to determine if the client knows the password and + return stale accordingly */ + if (noncecount != text->nonce_count) { + SETERROR(sparams->utils, + "incorrect nonce-count: authentication aborted"); + result = SASL_BADAUTH; + goto FreeAllMem; + } +#endif +#if 0 /* XXX Neither RFC 2617 nor RFC 2831 state that the cnonce + needs to remain constant for subsequent authentication to work */ + if (text->cnonce && strcmp((char *) cnonce, (char *) text->cnonce) != 0) { + SETERROR(sparams->utils, + "cnonce changed: authentication aborted"); + result = SASL_BADAUTH; + goto FreeAllMem; + } +#endif + } + + result = sparams->utils->prop_request(sparams->propctx, password_request); + if(result != SASL_OK) { + SETERROR(sparams->utils, "unable to obtain user password"); + goto FreeAllMem; + } + + /* this will trigger the getting of the aux properties */ + /* Note that if we don't have an authorization id, we don't use it... */ + + if (client_ignores_realm) { + if (strlen(text->realm) == 0) { + /* Don't put @ at the end of the username, if the realm is empty */ + _plug_strdup(sparams->utils, username, &full_username, NULL); + } else { + full_username = (char *) sparams->utils->malloc(strlen(username) + + strlen(text->realm) + 2); + full_username[0] = '\0'; + sprintf (full_username, "%s@%s", username, text->realm); + } + internal_username = full_username; + } else { + internal_username = username; + } + + canon_flags = SASL_CU_AUTHID; + if (!authorization_id || !*authorization_id) { + canon_flags |= SASL_CU_AUTHZID; + } + + result = sparams->canon_user(sparams->utils->conn, + internal_username, + 0, + canon_flags, + oparams); + if (result != SASL_OK) { + SETERROR(sparams->utils, "unable to canonify user and get auxprops"); + goto FreeAllMem; + } + + if (authorization_id != NULL && *authorization_id != '\0') { + result = sparams->canon_user(sparams->utils->conn, + authorization_id, 0, SASL_CU_AUTHZID, + oparams); + } + if (result != SASL_OK) { + SETERROR(sparams->utils, "unable to canonify authorization ID"); + goto FreeAllMem; + } + + result = sparams->utils->prop_getnames(sparams->propctx, password_request, + auxprop_values); + if (result < 0 || + ((!auxprop_values[0].name || !auxprop_values[0].values) +#if defined(OBSOLETE_DIGEST_ATTR) + && (!auxprop_values[1].name || !auxprop_values[1].values) +#endif + )) { + /* We didn't find this username */ + sparams->utils->seterror(sparams->utils->conn, 0, + "no secret in database"); + result = sparams->transition ? SASL_TRANS : SASL_NOUSER; + goto FreeAllMem; + } + + if (auxprop_values[0].name && auxprop_values[0].values) { + len = strlen(auxprop_values[0].values[0]); + if (len == 0) { + sparams->utils->seterror(sparams->utils->conn,0, + "empty secret"); + result = SASL_FAIL; + goto FreeAllMem; + } + + sec = sparams->utils->malloc(sizeof(sasl_secret_t) + len); + if (!sec) { + SETERROR(sparams->utils, "unable to allocate secret"); + result = SASL_FAIL; + goto FreeAllMem; + } + + sec->len = (unsigned) len; + strncpy((char *) sec->data, auxprop_values[0].values[0], len + 1); + + /* + * Verifying response obtained from client + * + * H_URP = H({ username-value,":",realm-value,":",passwd}) sec->data + * contains H_URP + */ + + /* Calculate the secret from the plaintext password */ + { + /* + * Secret = { H( { username-value, ":", realm-value, ":", passwd } ) } + * + * (used to build A1) + */ + + Try_8859_1 = DigestCalcSecret(sparams->utils, + (unsigned char *) username, + (unsigned char *) realm, + sec->data, + sec->len, + FALSE, + Secret); + Secret[HASHLEN] = '\0'; + } + if (Try_8859_1) { + /* + * Secret = { H( { username-value, ":", realm-value, ":", passwd } ) } + * + * (used to build A1) + */ + + DigestCalcSecret(sparams->utils, + (unsigned char *) username, + (unsigned char *) realm, + sec->data, + sec->len, + TRUE, + SecretBogus); + SecretBogus[HASHLEN] = '\0'; + } + + /* We're done with sec now. Let's get rid of it */ + _plug_free_secret(sparams->utils, &sec); +#if defined(OBSOLETE_DIGEST_ATTR) + } else if (auxprop_values[1].name && auxprop_values[1].values) { + /* NB: This will most likely fail for clients that + choose to ignore server-advertised realm */ + memcpy(Secret, auxprop_values[1].values[0], HASHLEN); + Secret[HASHLEN] = '\0'; +#endif + } else { + sparams->utils->seterror(sparams->utils->conn, 0, + "Have neither type of secret"); + return SASL_FAIL; + } + + /* erase the plaintext password */ + sparams->utils->prop_erase(sparams->propctx, password_request[0]); + + /* defaulting qop to "auth" if not specified */ + if (qop == NULL) { + _plug_strdup(sparams->utils, "auth", &qop, NULL); + } + + if (oparams->mech_ssf > 1) { + /* Remember the old cipher free function (if any). + It will be called later, once we are absolutely + sure that authentication was successful. */ + old_cipher_free = text->cipher_free; + /* free the old cipher context first */ + } + + /* check which layer/cipher to use */ + if ((!strcasecmp(qop, "auth-conf")) && (cipher != NULL)) { + /* see what cipher was requested */ + struct digest_cipher *cptr; + + cptr = available_ciphers; + while (cptr->name) { + /* find the cipher requested & make sure it's one we're happy + with by policy */ + if (!strcasecmp(cipher, cptr->name) && + stext->requiressf <= cptr->ssf && + stext->limitssf >= cptr->ssf) { + /* found it! */ + break; + } + cptr++; + } + + if (cptr->name) { + text->cipher_enc = cptr->cipher_enc; + text->cipher_dec = cptr->cipher_dec; + text->cipher_init = cptr->cipher_init; + text->cipher_free = cptr->cipher_free; + oparams->mech_ssf = cptr->ssf; + n = cptr->n; + } else { + /* erg? client requested something we didn't advertise! */ + sparams->utils->log(sparams->utils->conn, SASL_LOG_WARN, + "protocol violation: client requested invalid cipher"); + SETERROR(sparams->utils, "client requested invalid cipher"); + /* Mark that we attempted security layer negotiation */ + oparams->mech_ssf = 2; + result = SASL_FAIL; + goto FreeAllMem; + } + + oparams->encode=&digestmd5_encode; + oparams->decode=&digestmd5_decode; + } else if (!strcasecmp(qop, "auth-int") && + stext->requiressf <= 1 && stext->limitssf >= 1) { + oparams->encode = &digestmd5_encode; + oparams->decode = &digestmd5_decode; + oparams->mech_ssf = 1; + } else if (!strcasecmp(qop, "auth") && stext->requiressf == 0) { + oparams->encode = NULL; + oparams->decode = NULL; + oparams->mech_ssf = 0; + } else { + SETERROR(sparams->utils, + "protocol violation: client requested invalid qop"); + result = SASL_FAIL; + goto FreeAllMem; + } + + serverresponse = create_response(text, + sparams->utils, + nonce, + noncecount, + cnonce, + qop, + request, + Secret, + authorization_id, + &text->response_value); + + if (serverresponse == NULL) { + SETERROR(sparams->utils, "internal error: unable to create response"); + result = SASL_NOMEM; + goto FreeAllMem; + } + + /* if ok verified */ + if (strcmp(serverresponse, response) != 0) { + if (Try_8859_1) { + sparams->utils->free(serverresponse); + serverresponse = create_response(text, + sparams->utils, + nonce, + noncecount, + cnonce, + qop, + request, + SecretBogus, + authorization_id, + &text->response_value); + + if (serverresponse == NULL) { + SETERROR(sparams->utils, "internal error: unable to create response"); + result = SASL_NOMEM; + goto FreeAllMem; + } + + /* if ok verified */ + if (strcmp(serverresponse, response) != 0) { + SETERROR(sparams->utils, + "client response doesn't match what we generated (tried bogus)"); + result = SASL_BADAUTH; + + goto FreeAllMem; + } + + } else { + SETERROR(sparams->utils, + "client response doesn't match what we generated"); + result = SASL_BADAUTH; + + goto FreeAllMem; + } + } + + /* see if our nonce expired */ + if (!text->nonce || + (noncecount != text->nonce_count) || + (text->reauth->timeout && + time(0) - stext->timestamp > text->reauth->timeout)) { + if (!text->nonce) SETERROR(sparams->utils, "no cached server nonce"); + else if (noncecount != text->nonce_count) + SETERROR(sparams->utils, "incorrect nonce-count"); + else SETERROR(sparams->utils, "server nonce expired"); + stext->stale = 1; + result = SASL_BADAUTH; + + goto FreeAllMem; + } + + /* + * nothing more to do; authenticated set oparams information + */ + oparams->doneflag = 1; + oparams->maxoutbuf = client_maxbuf - 4; + if (oparams->mech_ssf > 1) { + /* MAC block (privacy) */ + oparams->maxoutbuf -= 25; + } else if(oparams->mech_ssf == 1) { + /* MAC block (integrity) */ + oparams->maxoutbuf -= 16; + } + + oparams->param_version = 0; + + text->seqnum = 0; /* for integrity/privacy */ + text->rec_seqnum = 0; /* for integrity/privacy */ + text->utils = sparams->utils; + + /* Free the old security layer, if any */ + if (old_cipher_free) old_cipher_free(text); + + /* used by layers */ + _plug_decode_init(&text->decode_context, text->utils, + sparams->props.maxbufsize ? sparams->props.maxbufsize : + DEFAULT_BUFSIZE); + + if (oparams->mech_ssf > 0) { + unsigned char enckey[16]; + unsigned char deckey[16]; + + create_layer_keys(text, sparams->utils,text->HA1,n,enckey,deckey); + + /* initialize cipher if need be */ + if (text->cipher_init) { + if (text->cipher_init(text, enckey, deckey) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "couldn't init cipher"); + } + } + } + + /* + * The server receives and validates the "digest-response". The server + * checks that the nonce-count is "00000001". If it supports subsequent + * authentication, it saves the value of the nonce and the nonce-count. + */ + + /* + * The "username-value", "realm-value" and "passwd" are encoded according + * to the value of the "charset" directive. If "charset=UTF-8" is + * present, and all the characters of either "username-value" or "passwd" + * are in the ISO 8859-1 character set, then it must be converted to + * UTF-8 before being hashed. A sample implementation of this conversion + * is in section 8. + */ + + /* add to challenge */ + { + unsigned resplen = 0; + + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "rspauth", (unsigned char *) text->response_value, + text->http_mode ? TRUE : FALSE) != SASL_OK) { + SETERROR(sparams->utils, "internal error: add_to_challenge failed"); + result = SASL_FAIL; + goto FreeAllMem; + } + + if (text->http_mode) { + /* per RFC 2617 */ + char ncvalue[10]; + + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "cnonce", cnonce, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllMem; + } + snprintf(ncvalue, sizeof(ncvalue), "%08x", text->nonce_count); + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "nc", (unsigned char *) ncvalue, FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllMem; + } + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "qop", (unsigned char *) qop, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllMem; + } + } + + /* self check */ + if (strlen(text->out_buf) > 2048) { + result = SASL_FAIL; + goto FreeAllMem; + } + } + + *serveroutlen = (unsigned) strlen(text->out_buf); + *serverout = text->out_buf; + + result = SASL_OK; + + FreeAllMem: + if (clientinlen > 0 && + text->reauth->timeout && + sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + + /* Look for an entry for the nonce value */ + unsigned val = hash((char *) nonce) % text->reauth->size; + + switch (result) { + case SASL_OK: + /* successful auth, setup for future reauth */ + if (text->nonce_count == 1) { + /* successful initial auth, create new entry */ + clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); + text->reauth->e[val].authid = username; username = NULL; + text->reauth->e[val].realm = text->realm; text->realm = NULL; + text->reauth->e[val].nonce = text->nonce; text->nonce = NULL; + text->reauth->e[val].cnonce = cnonce; cnonce = NULL; + } + if (text->nonce_count < text->reauth->e[val].nonce_count) { + /* paranoia. prevent replay attacks */ + clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); + } + else { + text->reauth->e[val].nonce_count = ++text->nonce_count; + text->reauth->e[val].u.s.timestamp = time(0); + } + break; + default: + if (text->nonce_count > 1) { + /* failed reauth, clear entry */ + clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); + } + else { + /* failed initial auth, leave existing cache */ + } + } + sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + /* free everything */ + if (in_start) sparams->utils->free (in_start); + + if (full_username != NULL) + sparams->utils->free (full_username); + if (username != NULL) + sparams->utils->free (username); + if (authorization_id != NULL) + sparams->utils->free (authorization_id); + if (realm != NULL) + sparams->utils->free (realm); + if (nonce != NULL) + sparams->utils->free (nonce); + if (cnonce != NULL) + sparams->utils->free (cnonce); + if (response != NULL) + sparams->utils->free (response); + if (cipher != NULL) + sparams->utils->free (cipher); + if (serverresponse != NULL) + sparams->utils->free(serverresponse); + if (charset != NULL) + sparams->utils->free (charset); + if (digesturi != NULL) + sparams->utils->free (digesturi); + if (qop!=NULL) + sparams->utils->free (qop); + if (sec) + _plug_free_secret(sparams->utils, &sec); + + return result; +} + +static int digestmd5_server_mech_step(void *conn_context, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) conn_context; + server_context_t *stext = (server_context_t *) conn_context; + + *serverout = NULL; + *serveroutlen = 0; + + if (clientinlen > 4096) return SASL_BADPROT; + + if (text == NULL) { + return SASL_BADPROT; + } + + switch (text->state) { + + case 1: + /* setup SSF limits */ + if (!text->http_mode && /* HTTP Digest doesn't need buffer */ + !sparams->props.maxbufsize) { + stext->limitssf = 0; + stext->requiressf = 0; + } else { + if (sparams->props.max_ssf < sparams->external_ssf) { + stext->limitssf = 0; + } else { + stext->limitssf = + sparams->props.max_ssf - sparams->external_ssf; + } + if (sparams->props.min_ssf < sparams->external_ssf) { + stext->requiressf = 0; + } else { + stext->requiressf = + sparams->props.min_ssf - sparams->external_ssf; + } + } + + if (clientin && text->reauth->timeout) { + /* here's where we attempt fast reauth if possible */ + if (digestmd5_server_mech_step2(stext, sparams, + clientin, clientinlen, + serverout, serveroutlen, + oparams) == SASL_OK) { + return SASL_OK; + } + + sparams->utils->log(NULL, SASL_LOG_WARN, + "DIGEST-MD5 reauth failed\n"); + + /* re-initialize everything for a fresh start */ + memset(oparams, 0, sizeof(sasl_out_params_t)); + if (text->nonce) sparams->utils->free(text->nonce); + if (text->realm) sparams->utils->free(text->realm); + text->realm = NULL; + text->nonce = NULL; + + /* fall through and issue challenge */ + } + + return digestmd5_server_mech_step1(stext, sparams, + clientin, clientinlen, + serverout, serveroutlen, oparams); + + case 2: + return digestmd5_server_mech_step2(stext, sparams, + clientin, clientinlen, + serverout, serveroutlen, oparams); + + default: + sparams->utils->log(NULL, SASL_LOG_ERR, + "Invalid DIGEST-MD5 server step %d\n", text->state); + return SASL_FAIL; + } + + return SASL_FAIL; /* should never get here */ +} + +static void digestmd5_server_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + server_context_t *stext = (server_context_t *) conn_context; + + if (!stext || !utils) return; + + digestmd5_common_mech_dispose(conn_context, utils); +} + +static sasl_server_plug_t digestmd5_server_plugins[] = +{ + { + "DIGEST-MD5", /* mech_name */ +#ifdef WITH_RC4 + 128, /* max_ssf */ +#elif defined(WITH_DES) + 112, +#else + 1, +#endif + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP, /* features */ + &server_glob_context, /* glob_context */ + &digestmd5_server_mech_new, /* mech_new */ + &digestmd5_server_mech_step, /* mech_step */ + &digestmd5_server_mech_dispose, /* mech_dispose */ + &digestmd5_common_mech_free, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech avail */ + NULL /* spare */ + } +}; + +int digestmd5_server_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + reauth_cache_t *reauth_cache; + const char *timeout = NULL; + unsigned int len; + + if (maxversion < SASL_SERVER_PLUG_VERSION) { + return SASL_BADVERS; + } + + /* reauth cache */ + reauth_cache = utils->malloc(sizeof(reauth_cache_t)); + if (reauth_cache == NULL) { + return SASL_NOMEM; + } + memset(reauth_cache, 0, sizeof(reauth_cache_t)); + reauth_cache->i_am = SERVER; + + /* fetch and canonify the reauth_timeout */ + utils->getopt(utils->getopt_context, "DIGEST-MD5", "reauth_timeout", + &timeout, &len); + if (timeout) { + reauth_cache->timeout = (time_t) 60 * strtol(timeout, NULL, 10); + } + if (reauth_cache->timeout < 0) { + reauth_cache->timeout = 0; + } + + if (reauth_cache->timeout) { + /* mutex */ + reauth_cache->mutex = utils->mutex_alloc(); + if (!reauth_cache->mutex) { + utils->free(reauth_cache); + return SASL_FAIL; + } + + /* entries */ + reauth_cache->size = 100; + reauth_cache->e = utils->malloc(reauth_cache->size * + sizeof(reauth_entry_t)); + if (reauth_cache->e == NULL) { + utils->mutex_free(reauth_cache->mutex); + utils->free(reauth_cache); + return SASL_NOMEM; + } + memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t)); + } + + ((digest_glob_context_t *) digestmd5_server_plugins[0].glob_context)->reauth = reauth_cache; + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = digestmd5_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + context_t common; + + sasl_secret_t *password; /* user password */ + unsigned int free_password; /* set if we need to free password */ + + int protection; + struct digest_cipher *cipher; + unsigned long server_maxbuf; + + /* for HTTP mode (RFC 2617) only */ + char *algorithm; + unsigned char *opaque; +} client_context_t; + +static digest_glob_context_t client_glob_context; + +/* calculate H(A1) as per spec */ +static void DigestCalcHA1(context_t * text, + const sasl_utils_t * utils, + char *pszAlg, + unsigned char *pszUserName, + unsigned char *pszRealm, + sasl_secret_t * pszPassword, + unsigned char *pszAuthorization_id, + unsigned char *pszNonce, + unsigned char *pszCNonce, + HASHHEX SessionKey) +{ + MD5_CTX Md5Ctx; + HASH HA1; + + DigestCalcSecret(utils, + pszUserName, + pszRealm, + (unsigned char *) pszPassword->data, + pszPassword->len, + FALSE, + HA1); + + if (!text->http_mode || /* per RFC 2831 */ + (pszAlg && strcasecmp(pszAlg, "md5-sess") == 0)) { /* per RFC 2617 */ + /* calculate the session key */ + utils->MD5Init(&Md5Ctx); + if (text->http_mode) { + /* per RFC 2617 Errata ID 1649 */ + HASHHEX HA1Hex; + + CvtHex(HA1, HA1Hex); + utils->MD5Update(&Md5Ctx, HA1Hex, HASHHEXLEN); + } + else { + /* per RFC 2831 */ + utils->MD5Update(&Md5Ctx, HA1, HASHLEN); + } + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce)); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce)); + if (pszAuthorization_id != NULL) { + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszAuthorization_id, + (unsigned) strlen((char *) pszAuthorization_id)); + } + utils->MD5Final(HA1, &Md5Ctx); + } + + CvtHex(HA1, SessionKey); + + /* xxx rc-* use different n */ + + /* save HA1 because we'll need it for the privacy and integrity keys */ + memcpy(text->HA1, HA1, sizeof(HASH)); + +} + +static char *calculate_response(context_t * text, + const sasl_utils_t * utils, + char *algorithm, + unsigned char *username, + unsigned char *realm, + unsigned char *nonce, + unsigned int ncvalue, + unsigned char *cnonce, + char *qop, + const sasl_http_request_t *request, + sasl_secret_t * passwd, + unsigned char *authorization_id, + char **response_value) +{ + HASHHEX SessionKey; + HASH EntityHash; + HASHHEX HEntity; + HASHHEX Response; + char *result; + + /* Verifing that all parameters was defined */ + if(!username || !cnonce || !nonce || !ncvalue || !request || !passwd) { + PARAMERROR( utils ); + return NULL; + } + + if (realm == NULL) { + /* a NULL realm is equivalent to the empty string */ + realm = (unsigned char *) ""; + } + + if (qop == NULL) { + /* default to a qop of just authentication */ + qop = "auth"; + } + + DigestCalcHA1(text, + utils, + algorithm, + username, + realm, + passwd, + authorization_id, + nonce, + cnonce, + SessionKey); + + if (text->http_mode) { + /* per RFC 2617 */ + MD5_CTX Md5Ctx; + + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, request->entity, request->elen); + utils->MD5Final(EntityHash, &Md5Ctx); + } + else { + /* per RFC 2831 */ + memset(EntityHash, 0, HASHLEN); + } + CvtHex(EntityHash, HEntity); + + DigestCalcResponse(utils, + SessionKey,/* HEX(H(A1)) */ + nonce, /* nonce from server */ + ncvalue, /* 8 hex digits */ + cnonce, /* client nonce */ + (unsigned char *) qop, /* qop-value: "", "auth", + * "auth-int" */ + (unsigned char *) request->uri, /* requested URL */ + (unsigned char *) request->method, + HEntity, /* H(entity body) if qop="auth-int" */ + Response /* request-digest or response-digest */ + ); + + result = utils->malloc(HASHHEXLEN + 1); + memcpy(result, Response, HASHHEXLEN); + result[HASHHEXLEN] = 0; + + if (response_value != NULL) { + char * new_response_value; + + DigestCalcResponse(utils, + SessionKey, /* HEX(H(A1)) */ + nonce, /* nonce from server */ + ncvalue, /* 8 hex digits */ + cnonce, /* client nonce */ + (unsigned char *) qop, /* qop-value: "", "auth", + * "auth-int" */ + (unsigned char *) request->uri, /* requested URL */ + NULL, + HEntity, /* H(entity body) if qop="auth-int" */ + Response /* request-digest or response-digest */ + ); + + new_response_value = utils->realloc(*response_value, HASHHEXLEN + 1); + if (new_response_value == NULL) { + free (*response_value); + *response_value = NULL; + return NULL; + } + *response_value = new_response_value; + + memcpy(*response_value, Response, HASHHEXLEN); + (*response_value)[HASHHEXLEN] = 0; + + } + + return result; +} + +static int make_client_response(context_t *text, + sasl_client_params_t *params, + sasl_out_params_t *oparams) +{ + client_context_t *ctext = (client_context_t *) text; + char *qop = NULL; + unsigned nbits = 0; + char *digesturi = NULL; + bool IsUTF8 = FALSE; + char ncvalue[10]; + char maxbufstr[64]; + char *response = NULL; + unsigned resplen = 0; + int result = SASL_OK; + cipher_free_t *old_cipher_free = NULL; + sasl_http_request_t rfc2831_request; + const sasl_http_request_t *request; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 make_client_response()"); + + if (oparams->mech_ssf > 1) { + /* Remember the old cipher free function (if any). + It will be called later, once we are absolutely + sure that authentication was successful. */ + old_cipher_free = text->cipher_free; + /* free the old cipher context first */ + } + + switch (ctext->protection) { + case DIGEST_PRIVACY: + qop = "auth-conf"; + oparams->encode = &digestmd5_encode; + oparams->decode = &digestmd5_decode; + oparams->mech_ssf = ctext->cipher->ssf; + + nbits = ctext->cipher->n; + text->cipher_enc = ctext->cipher->cipher_enc; + text->cipher_dec = ctext->cipher->cipher_dec; + text->cipher_free = ctext->cipher->cipher_free; + text->cipher_init = ctext->cipher->cipher_init; + break; + case DIGEST_INTEGRITY: + qop = "auth-int"; + oparams->encode = &digestmd5_encode; + oparams->decode = &digestmd5_decode; + oparams->mech_ssf = 1; + break; + case DIGEST_NOLAYER: + default: + qop = "auth"; + oparams->encode = NULL; + oparams->decode = NULL; + oparams->mech_ssf = 0; + } + + if (text->http_mode) { + /* per RFC 2617 (HTTP Request as set by calling application) */ + request = params->http_request; + } + else { + /* per RFC 2831 */ + digesturi = params->utils->malloc(strlen(params->service) + 1 + + strlen(params->serverFQDN) + 1 + + 1); + if (digesturi == NULL) { + result = SASL_NOMEM; + goto FreeAllocatedMem; + } + + /* allocated exactly this. safe */ + strcpy(digesturi, params->service); + strcat(digesturi, "/"); + strcat(digesturi, params->serverFQDN); + /* + * strcat (digesturi, "/"); strcat (digesturi, params->serverFQDN); + */ + + rfc2831_request.method = "AUTHENTICATE"; + rfc2831_request.uri = digesturi; + rfc2831_request.entity = NULL; + rfc2831_request.elen = 0; + rfc2831_request.non_persist = 0; + request = &rfc2831_request; + } + + /* response */ + response = + calculate_response(text, + params->utils, + ctext->algorithm, + (unsigned char *) oparams->authid, + (unsigned char *) text->realm, + text->nonce, + text->nonce_count, + text->cnonce, + qop, + request, + ctext->password, + strcmp(oparams->user, oparams->authid) ? + (unsigned char *) oparams->user : NULL, + &text->response_value); + + + resplen = 0; + if (text->out_buf) params->utils->free(text->out_buf); + text->out_buf = NULL; + text->out_buf_len = 0; + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "username", (unsigned char *) oparams->authid, + TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "realm", (unsigned char *) text->realm, + TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (strcmp(oparams->user, oparams->authid)) { + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "authzid", (unsigned char *) oparams->user, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "nonce", text->nonce, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "cnonce", text->cnonce, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + snprintf(ncvalue, sizeof(ncvalue), "%08x", text->nonce_count); + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "nc", (unsigned char *) ncvalue, FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "qop", (unsigned char *) qop, FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (ctext->cipher != NULL) { + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "cipher", + (unsigned char *) ctext->cipher->name, + FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } + + if (params->props.maxbufsize) { + snprintf(maxbufstr, sizeof(maxbufstr), "%d", params->props.maxbufsize); + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "maxbuf", (unsigned char *) maxbufstr, + FALSE) != SASL_OK) { + SETERROR(params->utils, + "internal error: add_to_challenge maxbuf failed"); + goto FreeAllocatedMem; + } + } + + if (IsUTF8) { + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "charset", (unsigned char *) "utf-8", + FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + text->http_mode ? "uri" /* per RFC 2617 */ + : "digest-uri", /* per RFC 2831 */ + (unsigned char *) request->uri, + TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (text->http_mode) { + /* per RFC 2617: algorithm & opaque MUST be sent back to server */ + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "algorithm", (unsigned char *) ctext->algorithm, + FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (ctext->opaque) { + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "opaque", ctext->opaque, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "response", (unsigned char *) response, + FALSE) != SASL_OK) { + + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* self check */ + if (strlen(text->out_buf) > 2048) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* set oparams */ + oparams->maxoutbuf = ctext->server_maxbuf; + if(oparams->mech_ssf > 1) { + /* MAC block (privacy) */ + oparams->maxoutbuf -= 25; + } else if(oparams->mech_ssf == 1) { + /* MAC block (integrity) */ + oparams->maxoutbuf -= 16; + } + + text->seqnum = 0; /* for integrity/privacy */ + text->rec_seqnum = 0; /* for integrity/privacy */ + text->utils = params->utils; + + /* Free the old security layer, if any */ + if (old_cipher_free) old_cipher_free(text); + + /* used by layers */ + _plug_decode_init(&text->decode_context, text->utils, + params->props.maxbufsize ? params->props.maxbufsize : + DEFAULT_BUFSIZE); + + if (oparams->mech_ssf > 0) { + unsigned char enckey[16]; + unsigned char deckey[16]; + + create_layer_keys(text, params->utils, text->HA1, nbits, + enckey, deckey); + + /* initialize cipher if need be */ + if (text->cipher_init) { + text->cipher_init(text, enckey, deckey); + } + } + + result = SASL_OK; + + FreeAllocatedMem: + if (digesturi) params->utils->free(digesturi); + if (response) params->utils->free(response); + + return result; +} + +static int parse_server_challenge(client_context_t *ctext, + sasl_client_params_t *params, + const char *serverin, unsigned serverinlen, + char ***outrealms, int *noutrealm) +{ + context_t *text = (context_t *) ctext; + int result = SASL_OK; + char *in_start = NULL; + char *in = NULL; + char **realms = NULL; + int nrealm = 0; + sasl_ssf_t limit, musthave = 0; + sasl_ssf_t external; + int protection = 0; + int saw_qop = 0; + int ciphers = 0; + int maxbuf_count = 0; + int algorithm_count = 0; + int opaque_count = 0; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 parse_server_challenge()"); + + if (!serverin || !serverinlen) { + params->utils->seterror(params->utils->conn, 0, + "no server challenge"); + return SASL_FAIL; + } + + in_start = in = params->utils->malloc(serverinlen + 1); + if (in == NULL) return SASL_NOMEM; + + memcpy(in, serverin, serverinlen); + in[serverinlen] = 0; + + ctext->server_maxbuf = 65536; /* Default value for maxbuf */ + + /* create a new cnonce */ + text->cnonce = create_nonce(params->utils); + if (text->cnonce == NULL) { + params->utils->seterror(params->utils->conn, 0, + "failed to create cnonce"); + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* parse the challenge */ + while (in[0] != '\0') { + char *name, *value; + + get_pair(&in, &name, &value); + + /* if parse error */ + if (name == NULL) { + params->utils->seterror(params->utils->conn, 0, "Parse error"); + result = SASL_BADAUTH; + goto FreeAllocatedMem; + } + + if (*name == '\0') { + break; + } + + if (strcasecmp(name, "realm") == 0) { + nrealm++; + + if(!realms) + realms = params->utils->malloc(sizeof(char *) * (nrealm + 1)); + else + realms = params->utils->realloc(realms, + sizeof(char *) * (nrealm + 1)); + + if (realms == NULL) { + result = SASL_NOMEM; + goto FreeAllocatedMem; + } + + _plug_strdup(params->utils, value, &realms[nrealm-1], NULL); + realms[nrealm] = NULL; + } else if (strcasecmp(name, "nonce") == 0) { + _plug_strdup(params->utils, value, (char **) &text->nonce, + NULL); + text->nonce_count = 1; + } else if (strcasecmp(name, "qop") == 0) { + saw_qop = 1; + while (value && *value) { + char *comma; + char *end_val; + +SKIP_SPACES_IN_QOP: + /* skipping spaces: */ + value = skip_lws(value); + if (*value == '\0') { + break; + } + + /* check for an extreme case when there is no data: LWSP ',' */ + if (*value == ',') { + value++; + goto SKIP_SPACES_IN_QOP; + } + + comma = strchr(value, ','); + + if (comma != NULL) { + *comma++ = '\0'; + } + + /* skip LWSP at the end of the value (if any), skip_r_lws returns pointer to + the first LWSP character, NUL (if there were none) or NULL if the value + is entirely from LWSP characters */ + end_val = skip_r_lws (value); + if (end_val == NULL) { + value = comma; + continue; + } else { + /* strip LWSP */ + *end_val = '\0'; + } + + if (strcasecmp(value, "auth-conf") == 0) { + protection |= DIGEST_PRIVACY; + } else if (strcasecmp(value, "auth-int") == 0) { + protection |= DIGEST_INTEGRITY; + } else if (strcasecmp(value, "auth") == 0) { + protection |= DIGEST_NOLAYER; + } else { + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "Server supports unknown layer: %s\n", + value); + } + + value = comma; + } + } else if (strcasecmp(name, "cipher") == 0) { + while (value && *value) { + struct digest_cipher *cipher = available_ciphers; + char *comma; + char *end_val; + +SKIP_SPACES_IN_CIPHER: + /* skipping spaces: */ + value = skip_lws(value); + if (*value == '\0') { + break; + } + + /* check for an extreme case when there is no data: LWSP ',' */ + if (*value == ',') { + value++; + goto SKIP_SPACES_IN_CIPHER; + } + + comma = strchr(value, ','); + + if (comma != NULL) { + *comma++ = '\0'; + } + + /* skip LWSP at the end of the value, skip_r_lws returns pointer to + the first LWSP character or NULL */ + end_val = skip_r_lws (value); + if (end_val == NULL) { + value = comma; + continue; + } else { + /* strip LWSP */ + *end_val = '\0'; + } + + /* do we support this cipher? */ + while (cipher->name) { + if (!strcasecmp(value, cipher->name)) break; + cipher++; + } + if (cipher->name) { + ciphers |= cipher->flag; + } else { + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "Server supports unknown cipher: %s\n", + value); + } + + value = comma; + } + } else if (strcasecmp(name, "stale") == 0 && ctext->password) { + /* clear any cached password */ + if (ctext->free_password) + _plug_free_secret(params->utils, &ctext->password); + ctext->password = NULL; + } else if (strcasecmp(name, "maxbuf") == 0) { + /* maxbuf A number indicating the size of the largest + * buffer the server is able to receive when using + * "auth-int". If this directive is missing, the default + * value is 65536. This directive may appear at most once; + * if multiple instances are present, the client should + * abort the authentication exchange. + */ + maxbuf_count++; + + if (maxbuf_count != 1) { + result = SASL_BADAUTH; + params->utils->seterror(params->utils->conn, 0, + "At least two maxbuf directives found. Authentication aborted"); + goto FreeAllocatedMem; + } + + if (str2ul32 (value, &ctext->server_maxbuf) == FALSE) { + result = SASL_BADAUTH; + params->utils->seterror(params->utils->conn, 0, + "Invalid maxbuf parameter received from server (%s)", value); + goto FreeAllocatedMem; + } + + if (ctext->server_maxbuf <= 16) { + result = SASL_BADAUTH; + params->utils->seterror(params->utils->conn, 0, + "Invalid maxbuf parameter received from server (too small: %s)", value); + goto FreeAllocatedMem; + } + + if (ctext->server_maxbuf > MAX_SASL_BUFSIZE) { + result = SASL_BADAUTH; + params->utils->seterror(params->utils->conn, 0, + "Invalid maxbuf parameter received from server (too big: %s)", value); + goto FreeAllocatedMem; + } + } else if (strcasecmp(name, "charset") == 0) { + if (strcasecmp(value, "utf-8") != 0) { + result = SASL_BADAUTH; + params->utils->seterror(params->utils->conn, 0, + "Charset must be UTF-8"); + goto FreeAllocatedMem; + } + } else if (strcasecmp(name,"algorithm")==0) { + if (text->http_mode && strcasecmp(value, "md5") == 0) { + /* per RFC 2617: need to support both "md5" and "md5-sess" */ + } + else if (strcasecmp(value, "md5-sess") != 0) + { + params->utils->seterror(params->utils->conn, 0, + "'algorithm' isn't 'md5-sess'"); + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + if (text->http_mode) { + /* per RFC 2617: algorithm MUST be saved */ + _plug_strdup(params->utils, value, (char **) &ctext->algorithm, + NULL); + } + algorithm_count++; + if (algorithm_count > 1) + { + params->utils->seterror(params->utils->conn, 0, + "Must see 'algorithm' only once"); + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } else if (strcasecmp(name,"opaque")==0) { + /* per RFC 2831: opaque MUST be ignored if received */ + if (text->http_mode) { + /* per RFC 2617: opaque MUST be saved */ + _plug_strdup(params->utils, value, (char **) &ctext->opaque, + NULL); + opaque_count++; + if (opaque_count > 1) + { + params->utils->seterror(params->utils->conn, 0, + "Must see 'opaque' only once"); + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } + } else { + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 unrecognized pair %s/%s: ignoring", + name, value); + } + } + + if (protection == 0) { + /* From RFC 2831[bis]: + This directive is optional; if not present it defaults to "auth". */ + if (saw_qop == 0) { + protection = DIGEST_NOLAYER; + } else { + result = SASL_BADAUTH; + params->utils->seterror(params->utils->conn, 0, + "Server doesn't support any known qop level"); + goto FreeAllocatedMem; + } + } + + if (algorithm_count != 1) { + params->utils->seterror(params->utils->conn, 0, + "Must see 'algorithm' once. Didn't see at all"); + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* make sure we have everything we require */ + if (text->nonce == NULL) { + params->utils->seterror(params->utils->conn, 0, + "Don't have nonce."); + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* get requested ssf */ + external = params->external_ssf; + + /* what do we _need_? how much is too much? */ + if (!text->http_mode && /* HTTP Digest doesn't need buffer */ + params->props.maxbufsize == 0) { + musthave = 0; + limit = 0; + } else { + if (params->props.max_ssf > external) { + limit = params->props.max_ssf - external; + } else { + limit = 0; + } + if (params->props.min_ssf > external) { + musthave = params->props.min_ssf - external; + } else { + musthave = 0; + } + } + + /* we now go searching for an option that gives us at least "musthave" + and at most "limit" bits of ssf. */ + if ((limit > 1) && (protection & DIGEST_PRIVACY)) { + struct digest_cipher *cipher; + + /* let's find an encryption scheme that we like */ + cipher = available_ciphers; + while (cipher->name) { + /* examine each cipher we support, see if it meets our security + requirements, and see if the server supports it. + choose the best one of these */ + if ((limit >= cipher->ssf) && (musthave <= cipher->ssf) && + (ciphers & cipher->flag) && + (!ctext->cipher || (cipher->ssf > ctext->cipher->ssf))) { + ctext->cipher = cipher; + } + cipher++; + } + + if (ctext->cipher) { + /* we found a cipher we like */ + ctext->protection = DIGEST_PRIVACY; + } else { + /* we didn't find any ciphers we like */ + params->utils->seterror(params->utils->conn, 0, + "No good privacy layers"); + } + } + + if (ctext->cipher == NULL) { + /* we failed to find an encryption layer we liked; + can we use integrity or nothing? */ + + if ((limit >= 1) && (musthave <= 1) + && (protection & DIGEST_INTEGRITY)) { + /* integrity */ + ctext->protection = DIGEST_INTEGRITY; + } else if (musthave <= 0) { + /* no layer */ + ctext->protection = DIGEST_NOLAYER; + + /* See if server supports not having a layer */ + if ((protection & DIGEST_NOLAYER) != DIGEST_NOLAYER) { + params->utils->seterror(params->utils->conn, 0, + "Server doesn't support \"no layer\""); + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } else { + params->utils->seterror(params->utils->conn, 0, + "Can't find an acceptable layer"); + result = SASL_TOOWEAK; + goto FreeAllocatedMem; + } + } + + *outrealms = realms; + *noutrealm = nrealm; + + FreeAllocatedMem: + if (in_start) params->utils->free(in_start); + + if (result != SASL_OK && realms) { + int lup; + + /* need to free all the realms */ + for (lup = 0;lup < nrealm; lup++) + params->utils->free(realms[lup]); + + params->utils->free(realms); + } + + return result; +} + +static int ask_user_info(client_context_t *ctext, + sasl_client_params_t *params, + char **realms, int nrealm, + sasl_interact_t **prompt_need, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) ctext; + int result = SASL_OK; + const char *authid = NULL, *userid = NULL, *realm = NULL; + char *realm_chal = NULL; + int user_result = SASL_OK; + int auth_result = SASL_OK; + int pass_result = SASL_OK; + int realm_result = SASL_FAIL; + int i; + size_t len; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 ask_user_info()"); + + /* try to get the authid */ + if (oparams->authid == NULL) { + auth_result = _plug_get_authid(params->utils, &authid, prompt_need); + + if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) { + return auth_result; + } + } + + /* try to get the userid */ + if (oparams->user == NULL) { + user_result = _plug_get_userid(params->utils, &userid, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { + return user_result; + } + } + + /* try to get the password */ + if (ctext->password == NULL) { + pass_result = _plug_get_password(params->utils, &ctext->password, + &ctext->free_password, prompt_need); + if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) { + return pass_result; + } + } + + /* try to get the realm */ + if (text->realm == NULL) { + if (realms) { + if(nrealm == 1) { + /* only one choice */ + realm = realms[0]; + realm_result = SASL_OK; + } else { + /* ask the user */ + realm_result = _plug_get_realm(params->utils, + (const char **) realms, + (const char **) &realm, + prompt_need); + } + } + + /* fake the realm if we must */ + if ((realm_result != SASL_OK) && (realm_result != SASL_INTERACT)) { + if (params->serverFQDN) { + realm = params->serverFQDN; + } else { + return realm_result; + } + } + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) || + (pass_result == SASL_INTERACT) || (realm_result == SASL_INTERACT)) { + + /* make our default realm */ + if (realm_result == SASL_INTERACT) { + if (realms) { + len = strlen(REALM_CHAL_PREFIX); + for (i = 0; i < nrealm; i++) { + len += strlen(realms[i]) + 4 /* " {}," */; + } + realm_chal = params->utils->malloc(len + 1); + strcpy (realm_chal, REALM_CHAL_PREFIX); + for (i = 0; i < nrealm; i++) { + strcat (realm_chal, " {"); + strcat (realm_chal, realms[i]); + strcat (realm_chal, "},"); + } + /* Replace the terminating comma with dot */ + realm_chal[len-1] = '.'; + + } else if (params->serverFQDN) { + realm_chal = params->utils->malloc(3+strlen(params->serverFQDN)); + if (realm_chal) { + sprintf(realm_chal, "{%s}", params->serverFQDN); + } else { + return SASL_NOMEM; + } + } + } + + /* make the prompt list */ + result = + _plug_make_prompts(params->utils, prompt_need, + user_result == SASL_INTERACT ? + "Please enter your authorization name" : NULL, + NULL, + auth_result == SASL_INTERACT ? + "Please enter your authentication name" : NULL, + NULL, + pass_result == SASL_INTERACT ? + "Please enter your password" : NULL, NULL, + NULL, NULL, NULL, + realm_chal ? realm_chal : "{}", + realm_result == SASL_INTERACT ? + "Please enter your realm" : NULL, + params->serverFQDN ? params->serverFQDN : NULL); + + if (result == SASL_OK) return SASL_INTERACT; + + return result; + } + + if (oparams->authid == NULL) { + if (!userid || !*userid) { + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + oparams); + } + else { + result = params->canon_user(params->utils->conn, + authid, 0, SASL_CU_AUTHID, oparams); + if (result != SASL_OK) return result; + + result = params->canon_user(params->utils->conn, + userid, 0, SASL_CU_AUTHZID, oparams); + } + if (result != SASL_OK) return result; + } + + /* Get an allocated version of the realm into the structure */ + if (realm && text->realm == NULL) { + _plug_strdup(params->utils, realm, (char **) &text->realm, NULL); + } + + return result; +} + +static int digestmd5_client_mech_new(void *glob_context, + sasl_client_params_t * params, + void **conn_context) +{ + context_t *text; + + if ((params->flags & SASL_NEED_HTTP) && !params->http_request) { + SETERROR(params->utils, + "DIGEST-MD5 unavailable due to lack of HTTP request"); + return SASL_BADPARAM; + } + + /* holds state are in -- allocate client size */ + text = params->utils->malloc(sizeof(client_context_t)); + if (text == NULL) + return SASL_NOMEM; + memset((client_context_t *)text, 0, sizeof(client_context_t)); + + text->state = 1; + text->i_am = CLIENT; + text->http_mode = (params->flags & SASL_NEED_HTTP); + text->reauth = ((digest_glob_context_t *) glob_context)->reauth; + + *conn_context = text; + + return SASL_OK; +} + +static int +digestmd5_client_mech_step1(client_context_t *ctext, + sasl_client_params_t *params, + const char *serverin __attribute__((unused)), + unsigned serverinlen __attribute__((unused)), + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) ctext; + int result = SASL_FAIL; + unsigned val; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 client step 1"); + + result = ask_user_info(ctext, params, NULL, 0, prompt_need, oparams); + if (result != SASL_OK) return result; + + /* check if we have cached info for this user on this server */ + val = hash(params->serverFQDN) % text->reauth->size; + if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + if (text->reauth->e[val].u.c.serverFQDN && + !strcasecmp(text->reauth->e[val].u.c.serverFQDN, + params->serverFQDN) && + !strcmp(text->reauth->e[val].authid, oparams->authid)) { + + /* we have info, so use it */ + if (text->realm) params->utils->free(text->realm); + _plug_strdup(params->utils, text->reauth->e[val].realm, + &text->realm, NULL); + _plug_strdup(params->utils, (char *) text->reauth->e[val].nonce, + (char **) &text->nonce, NULL); + text->nonce_count = ++text->reauth->e[val].nonce_count; + _plug_strdup(params->utils, (char *) text->reauth->e[val].cnonce, + (char **) &text->cnonce, NULL); + if (text->http_mode) { + /* per RFC 2617: algorithm & opaque MUST be sent back to server */ + _plug_strdup(params->utils, + (char *) text->reauth->e[val].u.c.algorithm, + (char **) &ctext->algorithm, NULL); + if (text->reauth->e[val].u.c.opaque) { + _plug_strdup(params->utils, + (char *) text->reauth->e[val].u.c.opaque, + (char **) &ctext->opaque, NULL); + } + } + ctext->protection = text->reauth->e[val].u.c.protection; + ctext->cipher = text->reauth->e[val].u.c.cipher; + ctext->server_maxbuf = text->reauth->e[val].u.c.server_maxbuf; + } + params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + if (!text->nonce) { + /* we don't have any reauth info, so just return + * that there is no initial client send */ + text->state = 2; + return SASL_CONTINUE; + } + + /* + * (username | realm | nonce | cnonce | nonce-count | qop digest-uri | + * response | maxbuf | charset | auth-param ) + */ + + result = make_client_response(text, params, oparams); + if (result != SASL_OK) return result; + + *clientoutlen = (unsigned) strlen(text->out_buf); + *clientout = text->out_buf; + + /* check for next state (2 or 3) is done in digestmd5_client_mech_step() */ + return SASL_CONTINUE; +} + +static int digestmd5_client_mech_step2(client_context_t *ctext, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) ctext; + int result = SASL_FAIL; + char **realms = NULL; + int nrealm = 0; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 client step 2"); + + if (params->props.min_ssf > params->props.max_ssf) { + return SASL_BADPARAM; + } + + /* don't bother parsing the challenge more than once */ + if (text->nonce == NULL) { + result = parse_server_challenge(ctext, params, serverin, serverinlen, + &realms, &nrealm); + if (result != SASL_OK) goto FreeAllocatedMem; + + if (nrealm == 1) { + /* only one choice! */ + if (text->realm) params->utils->free(text->realm); + text->realm = realms[0]; + + /* free realms */ + params->utils->free(realms); + realms = NULL; + } else { + /* Save realms for later use */ + text->realms = realms; + text->realm_cnt = nrealm; + } + } else { + /* Restore the list of realms */ + realms = text->realms; + nrealm = text->realm_cnt; + } + + result = ask_user_info(ctext, params, realms, nrealm, + prompt_need, oparams); + if (result != SASL_OK) goto FreeAllocatedMem; + + /* + * (username | realm | nonce | cnonce | nonce-count | qop | digest-uri | + * response | maxbuf | charset | auth-param ) + */ + + result = make_client_response(text, params, oparams); + if (result != SASL_OK) goto FreeAllocatedMem; + + *clientoutlen = (unsigned) strlen(text->out_buf); + *clientout = text->out_buf; + + text->state = 3; + + result = SASL_CONTINUE; + + FreeAllocatedMem: + return result; +} + +static int +digestmd5_client_mech_step3(client_context_t *ctext, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need __attribute__((unused)), + const char **clientout __attribute__((unused)), + unsigned *clientoutlen __attribute__((unused)), + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) ctext; + char *in = NULL; + char *in_start; + int result = SASL_FAIL; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 client step 3"); + + /* Verify that server is really what he claims to be */ + in_start = in = params->utils->malloc(serverinlen + 1); + if (in == NULL) return SASL_NOMEM; + + memcpy(in, serverin, serverinlen); + in[serverinlen] = 0; + + /* parse the response */ + while (in[0] != '\0') { + char *name, *value; + get_pair(&in, &name, &value); + + if (name == NULL) { + params->utils->seterror(params->utils->conn, 0, + "DIGEST-MD5 Received Garbage"); + result = SASL_BADAUTH; + break; + } + + if (*name == '\0') { + break; + } + + if (strcasecmp(name, "rspauth") == 0) { + + if (strcmp(text->response_value, value) != 0) { + params->utils->seterror(params->utils->conn, 0, + "DIGEST-MD5: This server wants us to believe that he knows shared secret"); + result = SASL_BADSERV; + } else { + oparams->doneflag = 1; + oparams->param_version = 0; + + result = SASL_OK; + } + break; + } else { + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 unrecognized pair %s/%s: ignoring", + name, value); + } + } + + params->utils->free(in_start); + + if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + unsigned val = hash(params->serverFQDN) % text->reauth->size; + switch (result) { + case SASL_OK: + if (text->nonce_count == 1) { + /* successful initial auth, setup for future reauth */ + clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); + _plug_strdup(params->utils, oparams->authid, + &text->reauth->e[val].authid, NULL); + text->reauth->e[val].realm = text->realm; text->realm = NULL; + text->reauth->e[val].nonce = text->nonce; text->nonce = NULL; + text->reauth->e[val].nonce_count = text->nonce_count; + text->reauth->e[val].cnonce = text->cnonce; text->cnonce = NULL; + _plug_strdup(params->utils, params->serverFQDN, + &text->reauth->e[val].u.c.serverFQDN, NULL); + if (text->http_mode) { + /* per RFC 2617: algorithm & opaque MUST be saved */ + text->reauth->e[val].u.c.algorithm = ctext->algorithm; + ctext->algorithm = NULL; + text->reauth->e[val].u.c.opaque = ctext->opaque; + ctext->opaque = NULL; + } + text->reauth->e[val].u.c.protection = ctext->protection; + text->reauth->e[val].u.c.cipher = ctext->cipher; + text->reauth->e[val].u.c.server_maxbuf = ctext->server_maxbuf; + } + else { + /* reauth, we already incremented nonce_count */ + } + break; + default: + if (text->nonce_count > 1) { + /* failed reauth, clear cache */ + clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); + } + else { + /* failed initial auth, leave existing cache */ + } + } + params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + return result; +} + +static int digestmd5_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) conn_context; + client_context_t *ctext = (client_context_t *) conn_context; + unsigned val = hash(params->serverFQDN) % text->reauth->size; + + if (serverinlen > 2048) return SASL_BADPROT; + + *clientout = NULL; + *clientoutlen = 0; + + switch (text->state) { + + case 1: + if (!serverin) { + /* here's where we attempt fast reauth if possible */ + int reauth = 0; + + /* check if we have saved info for this server */ + if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + reauth = text->reauth->e[val].u.c.serverFQDN && + !strcasecmp(text->reauth->e[val].u.c.serverFQDN, + params->serverFQDN); + params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + if (reauth) { + return digestmd5_client_mech_step1(ctext, params, + serverin, serverinlen, + prompt_need, + clientout, clientoutlen, + oparams); + } + else { + /* we don't have any reauth info, so just return + * that there is no initial client send */ + text->state = 2; + return SASL_CONTINUE; + } + } + else if (!strncasecmp(serverin, "rspauth=", 8)) { + /* server accepted fast reauth */ + text->state = 3; + goto step3; + } + + /* fall through and respond to challenge */ + text->state = 2; + + /* cleanup after a failed reauth attempt */ + if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); + + params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + if (text->realm) params->utils->free(text->realm); + if (text->nonce) params->utils->free(text->nonce); + if (text->cnonce) params->utils->free(text->cnonce); + text->realm = NULL; + text->nonce = text->cnonce = NULL; + ctext->cipher = NULL; + + GCC_FALLTHROUGH + + case 2: + return digestmd5_client_mech_step2(ctext, params, + serverin, serverinlen, + prompt_need, + clientout, clientoutlen, + oparams); + + case 3: + step3: + return digestmd5_client_mech_step3(ctext, params, + serverin, serverinlen, + prompt_need, + clientout, clientoutlen, + oparams); + + default: + params->utils->log(NULL, SASL_LOG_ERR, + "Invalid DIGEST-MD5 client step %d\n", text->state); + return SASL_FAIL; + } + + return SASL_FAIL; /* should never get here */ +} + +static void digestmd5_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + client_context_t *ctext = (client_context_t *) conn_context; + + if (!ctext || !utils) return; + + utils->log(utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 client mech dispose"); + + if (ctext->free_password) _plug_free_secret(utils, &ctext->password); + if (ctext->algorithm) utils->free(ctext->algorithm); + if (ctext->opaque) utils->free(ctext->opaque); + + digestmd5_common_mech_dispose(conn_context, utils); +} + +static sasl_client_plug_t digestmd5_client_plugins[] = +{ + { + "DIGEST-MD5", +#ifdef WITH_RC4 /* mech_name */ + 128, /* max ssf */ +#elif defined(WITH_DES) + 112, +#else + 1, +#endif + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_NEEDSERVERFQDN + | SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP, /* features */ + NULL, /* required_prompts */ + &client_glob_context, /* glob_context */ + &digestmd5_client_mech_new, /* mech_new */ + &digestmd5_client_mech_step, /* mech_step */ + &digestmd5_client_mech_dispose, /* mech_dispose */ + &digestmd5_common_mech_free, /* mech_free */ + NULL, /* idle */ + NULL, /* spare1 */ + NULL /* spare2 */ + } +}; + +int digestmd5_client_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + reauth_cache_t *reauth_cache; + + if (maxversion < SASL_CLIENT_PLUG_VERSION) + return SASL_BADVERS; + + /* reauth cache */ + reauth_cache = utils->malloc(sizeof(reauth_cache_t)); + if (reauth_cache == NULL) + return SASL_NOMEM; + memset(reauth_cache, 0, sizeof(reauth_cache_t)); + reauth_cache->i_am = CLIENT; + + /* mutex */ + reauth_cache->mutex = utils->mutex_alloc(); + if (!reauth_cache->mutex) + return SASL_FAIL; + + /* entries */ + reauth_cache->size = 10; + reauth_cache->e = utils->malloc(reauth_cache->size * + sizeof(reauth_entry_t)); + if (reauth_cache->e == NULL) + return SASL_NOMEM; + memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t)); + + ((digest_glob_context_t *) digestmd5_client_plugins[0].glob_context)->reauth = reauth_cache; + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = digestmd5_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/plugins/otp.c b/contrib/libs/sasl/plugins/otp.c new file mode 100644 index 0000000000..28d1f51fc2 --- /dev/null +++ b/contrib/libs/sasl/plugins/otp.c @@ -0,0 +1,1895 @@ +/* OTP SASL plugin + * Ken Murchison + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include <openssl/evp.h> +#include <openssl/md5.h> /* XXX hack for OpenBSD/OpenSSL cruftiness */ + +#include <sasl.h> +#define MD5_H /* suppress internal MD5 */ +#include <saslplug.h> + +#include "plugin_common.h" + +#ifdef macintosh +#error #include <sasl_otp_plugin_decl.h> +#endif + +/***************************** Common Section *****************************/ + +#define OTP_SEQUENCE_MAX 9999 +#define OTP_SEQUENCE_DEFAULT 499 +#define OTP_SEQUENCE_REINIT 490 +#define OTP_SEED_MIN 1 +#define OTP_SEED_MAX 16 +#define OTP_HASH_SIZE 8 /* 64 bits */ +#define OTP_CHALLENGE_MAX 100 +#define OTP_RESPONSE_MAX 100 +#define OTP_HEX_TYPE "hex:" +#define OTP_WORD_TYPE "word:" +#define OTP_INIT_HEX_TYPE "init-hex:" +#define OTP_INIT_WORD_TYPE "init-word:" + +typedef struct algorithm_option_s { + const char *name; /* name used in challenge/response */ + int swab; /* number of bytes to swab (0, 1, 2, 4, 8) */ + const char *evp_name; /* name used for lookup in EVP table */ +} algorithm_option_t; + +static algorithm_option_t algorithm_options[] = { + {"md4", 0, "md4"}, + {"md5", 0, "md5"}, + {"sha1", 4, "sha1"}, + {NULL, 0, NULL} +}; + +static EVP_MD_CTX *_plug_EVP_MD_CTX_new(const sasl_utils_t *utils) +{ + utils->log(NULL, SASL_LOG_DEBUG, "_plug_EVP_MD_CTX_new()"); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + return EVP_MD_CTX_new(); +#else + return utils->malloc(sizeof(EVP_MD_CTX)); +#endif +} + +static void _plug_EVP_MD_CTX_free(EVP_MD_CTX *ctx, const sasl_utils_t *utils) +{ + utils->log(NULL, SASL_LOG_DEBUG, "_plug_EVP_MD_CTX_free()"); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_MD_CTX_free(ctx); +#else + utils->free(ctx); +#endif +} + +/* Convert the binary data into ASCII hex */ +void bin2hex(unsigned char *bin, int binlen, char *hex) +{ + int i; + unsigned char c; + + for (i = 0; i < binlen; i++) { + c = (bin[i] >> 4) & 0xf; + hex[i*2] = (c > 9) ? ('a' + c - 10) : ('0' + c); + c = bin[i] & 0xf; + hex[i*2+1] = (c > 9) ? ('a' + c - 10) : ('0' + c); + } + hex[i*2] = '\0'; +} + +/* + * Hash the data using the given algorithm and fold it into 64 bits, + * swabbing bytes if necessary. + */ +static void otp_hash(const EVP_MD *md, char *in, size_t inlen, + unsigned char *out, int swab, EVP_MD_CTX *mdctx) +{ + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int i; + int j; + unsigned hashlen; + + EVP_DigestInit(mdctx, md); + EVP_DigestUpdate(mdctx, in, inlen); + EVP_DigestFinal(mdctx, hash, &hashlen); + + /* Fold the result into 64 bits */ + for (i = OTP_HASH_SIZE; i < hashlen; i++) { + hash[i % OTP_HASH_SIZE] ^= hash[i]; + } + + /* Swab bytes */ + if (swab) { + for (i = 0; i < OTP_HASH_SIZE;) { + for (j = swab-1; j > -swab; i++, j-=2) + out[i] = hash[i+j]; + } + } + else + memcpy(out, hash, OTP_HASH_SIZE); +} + +static int generate_otp(const sasl_utils_t *utils, + algorithm_option_t *alg, unsigned seq, char *seed, + unsigned char *secret, unsigned secret_len, + unsigned char *otp) +{ + const EVP_MD *md; + EVP_MD_CTX *mdctx = NULL; + char *key = NULL; + int r = SASL_OK; + + if (!(md = EVP_get_digestbyname(alg->evp_name))) { + utils->seterror(utils->conn, 0, + "OTP algorithm %s is not available", alg->evp_name); + return SASL_FAIL; + } + + if ((mdctx = _plug_EVP_MD_CTX_new(utils)) == NULL) { + SETERROR(utils, "cannot allocate MD CTX"); + r = SASL_NOMEM; + goto done; + } + + if ((key = utils->malloc(strlen(seed) + secret_len + 1)) == NULL) { + SETERROR(utils, "cannot allocate OTP key"); + r = SASL_NOMEM; + goto done; + } + + /* initial step */ + sprintf(key, "%s%.*s", seed, secret_len, secret); + otp_hash(md, key, strlen(key), otp, alg->swab, mdctx); + + /* computation step */ + while (seq-- > 0) + otp_hash(md, (char *) otp, OTP_HASH_SIZE, otp, alg->swab, mdctx); + +done: + if (key) utils->free(key); + if (mdctx) _plug_EVP_MD_CTX_free(mdctx, utils); + + return r; +} + +static int parse_challenge(const sasl_utils_t *utils, + char *chal, algorithm_option_t **alg, + unsigned *seq, char *seed, int is_init) +{ + char *c; + algorithm_option_t *opt; + int n; + + c = chal; + + /* eat leading whitespace */ + while (*c && isspace((int) *c)) c++; + + if (!is_init) { + /* check the prefix */ + if (!*c || strncmp(c, "otp-", 4)) { + SETERROR(utils, "not an OTP challenge"); + return SASL_BADPROT; + } + + /* skip the prefix */ + c += 4; + } + + /* find the algorithm */ + opt = algorithm_options; + while (opt->name) { + if (!strncmp(c, opt->name, strlen(opt->name))) { + break; + } + opt++; + } + + /* didn't find the algorithm in our list */ + if (!opt->name) { + utils->seterror(utils->conn, 0, "OTP algorithm '%s' not supported", c); + return SASL_BADPROT; + } + + /* skip algorithm name */ + c += strlen(opt->name); + *alg = opt; + + /* eat whitespace */ + if (!isspace((int) *c)) { + SETERROR(utils, "no whitespace between OTP algorithm and sequence"); + return SASL_BADPROT; + } + while (*c && isspace((int) *c)) c++; + + /* grab the sequence */ + if ((*seq = strtoul(c, &c, 10)) > OTP_SEQUENCE_MAX) { + utils->seterror(utils->conn, 0, "sequence > %u", OTP_SEQUENCE_MAX); + return SASL_BADPROT; + } + + /* eat whitespace */ + if (!isspace((int) *c)) { + SETERROR(utils, "no whitespace between OTP sequence and seed"); + return SASL_BADPROT; + } + while (*c && isspace((int) *c)) c++; + + /* grab the seed, converting to lowercase as we go */ + n = 0; + while (*c && isalnum((int) *c) && (n < OTP_SEED_MAX)) + seed[n++] = tolower((int) *c++); + if (n > OTP_SEED_MAX) { + utils->seterror(utils->conn, 0, "OTP seed length > %u", OTP_SEED_MAX); + return SASL_BADPROT; + } + else if (n < OTP_SEED_MIN) { + utils->seterror(utils->conn, 0, "OTP seed length < %u", OTP_SEED_MIN); + return SASL_BADPROT; + } + seed[n] = '\0'; + + if (!is_init) { + /* eat whitespace */ + if (!isspace((int) *c)) { + SETERROR(utils, "no whitespace between OTP seed and extensions"); + return SASL_BADPROT; + } + while (*c && isspace((int) *c)) c++; + + /* make sure this is an extended challenge */ + if (strncmp(c, "ext", 3) || + (*(c+=3) && + !(isspace((int) *c) || (*c == ',') || + (*c == '\r') || (*c == '\n')))) { + SETERROR(utils, "not an OTP extended challenge"); + return SASL_BADPROT; + } + } + + return SASL_OK; +} + +static void +otp_common_mech_free(void *global_context __attribute__((unused)), + const sasl_utils_t *utils __attribute__((unused))) +{ + /* Don't call EVP_cleanup(); here, as this might confuse the calling + application if it also uses OpenSSL */ +} + +/***************************** Server Section *****************************/ + +#ifdef HAVE_OPIE +#include <opie.h> +#endif + +typedef struct server_context { + int state; + + char *authid; + int locked; /* is the user's secret locked? */ + algorithm_option_t *alg; +#ifdef HAVE_OPIE + struct opie opie; +#else + char *realm; + unsigned seq; + char seed[OTP_SEED_MAX+1]; + unsigned char otp[OTP_HASH_SIZE]; + time_t timestamp; /* time we locked the secret */ +#endif /* HAVE_OPIE */ + + char *out_buf; + unsigned out_buf_len; +} server_context_t; + +static int otp_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + server_context_t *text; + + /* holds state are in */ + text = sparams->utils->malloc(sizeof(server_context_t)); + if (text == NULL) { + MEMERROR(sparams->utils); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(server_context_t)); + + text->state = 1; + + *conn_context = text; + + return SASL_OK; +} + +#ifdef HAVE_OPIE + +#ifndef OPIE_KEYFILE +#define OPIE_KEYFILE "/etc/opiekeys" +#endif + +static int opie_server_mech_step(void *conn_context, + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + server_context_t *text = (server_context_t *) conn_context; + + *serverout = NULL; + *serveroutlen = 0; + + if (text == NULL) { + return SASL_BADPROT; + } + + switch (text->state) { + + case 1: { + const char *authzid; + const char *authid; + size_t authid_len; + unsigned lup = 0; + int result; + + /* should have received authzid NUL authid */ + + /* get authzid */ + authzid = clientin; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + if (lup >= clientinlen) { + SETERROR(params->utils, "Can only find OTP authzid (no authid)"); + return SASL_BADPROT; + } + + /* get authid */ + ++lup; + authid = clientin + lup; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + authid_len = clientin + lup - authid; + + if (lup != clientinlen) { + SETERROR(params->utils, + "Got more data than we were expecting in the OTP plugin\n"); + return SASL_BADPROT; + } + + text->authid = params->utils->malloc(authid_len + 1); + if (text->authid == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + /* we can't assume that authen is null-terminated */ + strncpy(text->authid, authid, authid_len); + text->authid[authid_len] = '\0'; + + result = params->canon_user(params->utils->conn, text->authid, 0, + SASL_CU_AUTHID, oparams); + if (result != SASL_OK) return result; + + result = params->canon_user(params->utils->conn, + strlen(authzid) ? authzid : text->authid, + 0, SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + + result = _plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), OTP_CHALLENGE_MAX+1); + if (result != SASL_OK) return result; + + /* create challenge - return sasl_continue on success */ + result = opiechallenge(&text->opie, text->authid, text->out_buf); + + switch (result) { + case 0: + text->locked = 1; + + *serverout = text->out_buf; + *serveroutlen = strlen(text->out_buf); + + text->state = 2; + return SASL_CONTINUE; + + case 1: + SETERROR(params->utils, "opiechallenge: user not found or locked"); + return SASL_NOUSER; + + default: + SETERROR(params->utils, + "opiechallenge: system error (file, memory, I/O)"); + return SASL_FAIL; + } + } + + case 2: { + char response[OPIE_RESPONSE_MAX+1]; + int result; + + /* should have received extended response, + but we'll take anything that we can verify */ + + if (clientinlen > OPIE_RESPONSE_MAX) { + SETERROR(params->utils, "response too long"); + return SASL_BADPROT; + } + + /* we can't assume that the response is null-terminated */ + strncpy(response, clientin, clientinlen); + response[clientinlen] = '\0'; + + /* verify response */ + result = opieverify(&text->opie, response); + text->locked = 0; + + switch (result) { + case 0: + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; + + case 1: + SETERROR(params->utils, "opieverify: invalid/incorrect response"); + return SASL_BADAUTH; + + default: + SETERROR(params->utils, + "opieverify: system error (file, memory, I/O)"); + return SASL_FAIL; + } + } + + default: + params->utils->log(NULL, SASL_LOG_ERR, + "Invalid OTP server step %d\n", text->state); + return SASL_FAIL; + } + + return SASL_FAIL; /* should never get here */ +} + +static void opie_server_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + server_context_t *text = (server_context_t *) conn_context; + + if (!text) return; + + /* if we created a challenge, but bailed before the verification of the + response, do a verify here to release the lock on the user key */ + if (text->locked) opieverify(&text->opie, ""); + + if (text->authid) _plug_free_string(utils, &(text->authid)); + + if (text->out_buf) utils->free(text->out_buf); + + utils->free(text); +} + +static int opie_mech_avail(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + void **conn_context __attribute__((unused))) +{ + const char *fname; + unsigned int len; + + sparams->utils->getopt(sparams->utils->getopt_context, + "OTP", "opiekeys", &fname, &len); + + if (!fname) fname = OPIE_KEYFILE; + + if (access(fname, R_OK|W_OK) != 0) { + sparams->utils->log(NULL, SASL_LOG_ERR, + "OTP unavailable because " + "can't read/write key database %s: %m", + fname, errno); + return SASL_NOMECH; + } + + return SASL_OK; +} + +static sasl_server_plug_t otp_server_plugins[] = +{ + { + "OTP", + 0, + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_FORWARD_SECRECY, + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_DONTUSE_USERPASSWD + | SASL_FEAT_ALLOWS_PROXY, + NULL, + &otp_server_mech_new, + &opie_server_mech_step, + &opie_server_mech_dispose, + &otp_common_mech_free, + NULL, + NULL, + NULL, + &opie_mech_avail, + NULL + } +}; +#else /* HAVE_OPIE */ + +#include "otp.h" + +#define OTP_MDA_DEFAULT "md5" +#define OTP_LOCK_TIMEOUT 5 * 60 /* 5 minutes */ + +/* Convert the ASCII hex into binary data */ +int hex2bin(char *hex, unsigned char *bin, int binlen) +{ + int i; + char *c; + unsigned char msn, lsn; + + memset(bin, 0, binlen); + + for (c = hex, i = 0; i < binlen; c++) { + /* whitespace */ + if (isspace((int) *c)) + continue; + /* end of string, or non-hex char */ + if (!*c || !*(c+1) || !isxdigit((int) *c)) + break; + + msn = (*c > '9') ? tolower((int) *c) - 'a' + 10 : *c - '0'; + c++; + lsn = (*c > '9') ? tolower((int) *c) - 'a' + 10 : *c - '0'; + + bin[i++] = (unsigned char) (msn << 4) | lsn; + } + + return (i < binlen) ? SASL_BADAUTH : SASL_OK; +} + +static int make_secret(const sasl_utils_t *utils, const char *alg, + unsigned seq, char *seed, unsigned char *otp, + time_t timeout, sasl_secret_t **secret) +{ + size_t sec_len; + char *data; + char buf[2*OTP_HASH_SIZE+1]; + + /* + * secret is stored as: + * + * <alg> \t <seq> \t <seed> \t <otp> \t <timeout> \0 + * + * <timeout> is used as a "lock" when an auth is in progress + * we just set it to zero here (no lock) + */ + sec_len = strlen(alg)+1+4+1+strlen(seed)+1+2*OTP_HASH_SIZE+1+20+1; + *secret = utils->malloc(sizeof(sasl_secret_t)+sec_len); + if (!*secret) { + return SASL_NOMEM; + } + + (*secret)->len = (unsigned) sec_len; + data = (char *) (*secret)->data; + + bin2hex(otp, OTP_HASH_SIZE, buf); + buf[2*OTP_HASH_SIZE] = '\0'; + + sprintf(data, "%s\t%04d\t%s\t%s\t%020ld", + alg, seq, seed, buf, timeout); + + return SASL_OK; +} + +static int parse_secret(const sasl_utils_t *utils, + char *secret, size_t seclen, + char *alg, unsigned *seq, char *seed, + unsigned char *otp, + time_t *timeout) +{ + if (strlen(secret) < seclen) { + char *c; + + /* + * old-style (binary) secret is stored as: + * + * <alg> \0 <seq> \0 <seed> \0 <otp> <timeout> + * + */ + + if (seclen < (3+1+1+1+OTP_SEED_MIN+1+OTP_HASH_SIZE+sizeof(time_t))) { + SETERROR(utils, "OTP secret too short"); + return SASL_FAIL; + } + + c = secret; + + strcpy(alg, (char*) c); + c += strlen(alg)+1; + + *seq = strtoul(c, NULL, 10); + c += 5; + + strcpy(seed, (char*) c); + c += strlen(seed)+1; + + memcpy(otp, c, OTP_HASH_SIZE); + c += OTP_HASH_SIZE; + + memcpy(timeout, c, sizeof(time_t)); + + return SASL_OK; + } + + else { + char buf[2*OTP_HASH_SIZE+1]; + + /* + * new-style (ASCII) secret is stored as: + * + * <alg> \t <seq> \t <seed> \t <otp> \t <timeout> \0 + * + */ + + if (seclen < (3+1+1+1+OTP_SEED_MIN+1+2*OTP_HASH_SIZE+1+20)) { + SETERROR(utils, "OTP secret too short"); + return SASL_FAIL; + } + + sscanf(secret, "%s\t%04d\t%s\t%s\t%020ld", + alg, seq, seed, buf, timeout); + + hex2bin(buf, otp, OTP_HASH_SIZE); + + return SASL_OK; + } +} + +/* Compare two string pointers */ +static int strptrcasecmp(const void *arg1, const void *arg2) +{ + return (strcasecmp(*((char**) arg1), *((char**) arg2))); +} + +/* Convert the 6 words into binary data */ +static int word2bin(const sasl_utils_t *utils, + char *words, unsigned char *bin, const EVP_MD *md, + EVP_MD_CTX *mdctx) +{ + int i, j; + char *c, *word, buf[OTP_RESPONSE_MAX+1]; + void *base; + int nmemb; + unsigned long x = 0; + unsigned char bits[OTP_HASH_SIZE+1]; /* 1 for checksum */ + unsigned char chksum; + int bit, fbyte, lbyte; + const char **str_ptr; + int alt_dict = 0; + + /* this is a destructive operation, so make a work copy */ + strcpy(buf, words); + memset(bits, 0, 9); + + for (c = buf, bit = 0, i = 0; i < 6; i++, c++, bit+=11) { + while (*c && isspace((int) *c)) c++; + word = c; + while (*c && isalpha((int) *c)) c++; + if (!*c && i < 5) break; + *c = '\0'; + if (strlen(word) < 1 || strlen(word) > 4) { + utils->log(NULL, SASL_LOG_DEBUG, + "incorrect word length '%s'", word); + return SASL_BADAUTH; + } + + /* standard dictionary */ + if (!alt_dict) { + if (strlen(word) < 4) { + base = otp_std_dict; + nmemb = OTP_4LETTER_OFFSET; + } + else { + base = otp_std_dict + OTP_4LETTER_OFFSET; + nmemb = OTP_STD_DICT_SIZE - OTP_4LETTER_OFFSET; + } + + str_ptr = (const char**) bsearch((void*) &word, base, nmemb, + sizeof(const char*), + strptrcasecmp); + if (str_ptr) { + x = (unsigned long) (str_ptr - otp_std_dict); + } + else if (i == 0) { + /* couldn't find first word, try alternate dictionary */ + alt_dict = 1; + } + else { + utils->log(NULL, SASL_LOG_DEBUG, + "word '%s' not found in dictionary", word); + return SASL_BADAUTH; + } + } + + /* alternate dictionary */ + if (alt_dict) { + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned hashlen; + + EVP_DigestInit(mdctx, md); + EVP_DigestUpdate(mdctx, word, strlen(word)); + EVP_DigestFinal(mdctx, hash, &hashlen); + + /* use lowest 11 bits */ + x = ((hash[hashlen-2] & 0x7) << 8) | hash[hashlen-1]; + } + + /* left align 11 bits on byte boundary */ + x <<= (8 - ((bit+11) % 8)); + /* first output byte containing some of our 11 bits */ + fbyte = bit / 8; + /* last output byte containing some of our 11 bits */ + lbyte = (bit+11) / 8; + /* populate the output bytes with the 11 bits */ + for (j = lbyte; j >= fbyte; j--, x >>= 8) + bits[j] |= (unsigned char) (x & 0xff); + } + + if (i < 6) { + utils->log(NULL, SASL_LOG_DEBUG, "not enough words (%d)", i); + return SASL_BADAUTH; + } + + /* see if the 2-bit checksum is correct */ + for (chksum = 0, i = 0; i < 8; i++) { + for (j = 0; j < 4; j++) { + chksum += ((bits[i] >> (2 * j)) & 0x3); + } + } + chksum <<= 6; + + if (chksum != bits[8]) { + utils->log(NULL, SASL_LOG_DEBUG, "incorrect parity"); + return SASL_BADAUTH; + } + + memcpy(bin, bits, OTP_HASH_SIZE); + + return SASL_OK; +} + +static int verify_response(server_context_t *text, const sasl_utils_t *utils, + char *response) +{ + const EVP_MD *md; + EVP_MD_CTX *mdctx = NULL; + char *c; + int do_init = 0; + unsigned char cur_otp[OTP_HASH_SIZE], prev_otp[OTP_HASH_SIZE]; + int r; + + /* find the MDA */ + if (!(md = EVP_get_digestbyname(text->alg->evp_name))) { + utils->seterror(utils->conn, 0, + "OTP algorithm %s is not available", + text->alg->evp_name); + return SASL_FAIL; + } + + if ((mdctx = _plug_EVP_MD_CTX_new(utils)) == NULL) { + SETERROR(utils, "cannot allocate MD CTX"); + return SASL_NOMEM; + } + + /* eat leading whitespace */ + c = response; + while (isspace((int) *c)) c++; + + if (strchr(c, ':')) { + if (!strncasecmp(c, OTP_HEX_TYPE, strlen(OTP_HEX_TYPE))) { + r = hex2bin(c+strlen(OTP_HEX_TYPE), cur_otp, OTP_HASH_SIZE); + } + else if (!strncasecmp(c, OTP_WORD_TYPE, strlen(OTP_WORD_TYPE))) { + r = word2bin(utils, c+strlen(OTP_WORD_TYPE), cur_otp, md, mdctx); + } + else if (!strncasecmp(c, OTP_INIT_HEX_TYPE, + strlen(OTP_INIT_HEX_TYPE))) { + do_init = 1; + r = hex2bin(c+strlen(OTP_INIT_HEX_TYPE), cur_otp, OTP_HASH_SIZE); + } + else if (!strncasecmp(c, OTP_INIT_WORD_TYPE, + strlen(OTP_INIT_WORD_TYPE))) { + do_init = 1; + r = word2bin(utils, c+strlen(OTP_INIT_WORD_TYPE), cur_otp, md, mdctx); + } + else { + SETERROR(utils, "unknown OTP extended response type"); + r = SASL_BADAUTH; + } + } + else { + /* standard response, try word first, and then hex */ + r = word2bin(utils, c, cur_otp, md, mdctx); + if (r != SASL_OK) + r = hex2bin(c, cur_otp, OTP_HASH_SIZE); + } + + if (r == SASL_OK) { + /* do one more hash (previous otp) and compare to stored otp */ + otp_hash(md, (char *) cur_otp, OTP_HASH_SIZE, + prev_otp, text->alg->swab, mdctx); + + if (!memcmp(prev_otp, text->otp, OTP_HASH_SIZE)) { + /* update the secret with this seq/otp */ + memcpy(text->otp, cur_otp, OTP_HASH_SIZE); + text->seq--; + r = SASL_OK; + } + else + r = SASL_BADAUTH; + } + + /* if this is an init- attempt, let's check it out */ + if (r == SASL_OK && do_init) { + char *new_chal = NULL, *new_resp = NULL; + algorithm_option_t *alg; + unsigned seq; + char seed[OTP_SEED_MAX+1]; + unsigned char new_otp[OTP_HASH_SIZE]; + + /* find the challenge and response fields */ + new_chal = strchr(c+strlen(OTP_INIT_WORD_TYPE), ':'); + if (new_chal) { + *new_chal++ = '\0'; + new_resp = strchr(new_chal, ':'); + if (new_resp) + *new_resp++ = '\0'; + } + + if (!(new_chal && new_resp)) { + r = SASL_BADAUTH; + goto done; + } + + if ((r = parse_challenge(utils, new_chal, &alg, &seq, seed, 1)) + != SASL_OK) { + goto done; + } + + if (seq < 1 || !strcasecmp(seed, text->seed)) { + r = SASL_BADAUTH; + goto done; + } + + /* find the MDA */ + if (!(md = EVP_get_digestbyname(alg->evp_name))) { + utils->seterror(utils->conn, 0, + "OTP algorithm %s is not available", + alg->evp_name); + r = SASL_BADAUTH; + goto done; + } + + if (!strncasecmp(c, OTP_INIT_HEX_TYPE, strlen(OTP_INIT_HEX_TYPE))) { + r = hex2bin(new_resp, new_otp, OTP_HASH_SIZE); + } + else if (!strncasecmp(c, OTP_INIT_WORD_TYPE, + strlen(OTP_INIT_WORD_TYPE))) { + r = word2bin(utils, new_resp, new_otp, md, mdctx); + } + + if (r == SASL_OK) { + /* setup for new secret */ + text->alg = alg; + text->seq = seq; + strcpy(text->seed, seed); + memcpy(text->otp, new_otp, OTP_HASH_SIZE); + } + } + + done: + if (mdctx) _plug_EVP_MD_CTX_free(mdctx, utils); + + return r; +} + +static int otp_server_mech_step1(server_context_t *text, + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + const char *authzid; + const char *authidp; + size_t authid_len; + unsigned lup = 0; + int result, n; + const char *lookup_request[] = { "*cmusaslsecretOTP", + NULL }; + const char *store_request[] = { "cmusaslsecretOTP", + NULL }; + struct propval auxprop_values[2]; + char mda[10]; + time_t timeout; + sasl_secret_t *sec = NULL; + struct propctx *propctx = NULL; + + /* should have received authzid NUL authid */ + + /* get authzid */ + authzid = clientin; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + if (lup >= clientinlen) { + SETERROR(params->utils, "Can only find OTP authzid (no authid)"); + return SASL_BADPROT; + } + + /* get authid */ + ++lup; + authidp = clientin + lup; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + authid_len = clientin + lup - authidp; + + if (lup != clientinlen) { + SETERROR(params->utils, + "Got more data than we were expecting in the OTP plugin\n"); + return SASL_BADPROT; + } + + text->authid = params->utils->malloc(authid_len + 1); + if (text->authid == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + /* we can't assume that authid is null-terminated */ + strncpy(text->authid, authidp, authid_len); + text->authid[authid_len] = '\0'; + + n = 0; + do { + /* Get user secret */ + result = params->utils->prop_request(params->propctx, + lookup_request); + if (result != SASL_OK) return result; + + /* this will trigger the getting of the aux properties. + Must use the fully qualified authid here */ + result = params->canon_user(params->utils->conn, text->authid, 0, + SASL_CU_AUTHID, oparams); + if (result != SASL_OK) return result; + + result = params->canon_user(params->utils->conn, + strlen(authzid) ? authzid : text->authid, + 0, SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + + result = params->utils->prop_getnames(params->propctx, + lookup_request, + auxprop_values); + if (result < 0 || + (!auxprop_values[0].name || !auxprop_values[0].values)) { + /* We didn't find this username */ + SETERROR(params->utils, "no OTP secret in database"); + result = params->transition ? SASL_TRANS : SASL_NOUSER; + return (result); + } + + if (auxprop_values[0].name && auxprop_values[0].values) { + result = parse_secret(params->utils, + (char*) auxprop_values[0].values[0], + auxprop_values[0].valsize, + mda, &text->seq, text->seed, text->otp, + &timeout); + + if (result != SASL_OK) return result; + } else { + SETERROR(params->utils, "don't have an OTP secret"); + return SASL_FAIL; + } + + text->timestamp = time(0); + } + /* + * check lock timeout + * + * we try 10 times in 1 second intervals in order to give the other + * auth attempt time to finish + */ + while ((text->timestamp < timeout) && (n++ < 10) && !sleep(1)); + + if (text->timestamp < timeout) { + SETERROR(params->utils, + "simultaneous OTP authentications not permitted"); + return SASL_TRYAGAIN; + } + + /* check sequence number */ + if (text->seq <= 1) { + SETERROR(params->utils, "OTP has expired (sequence <= 1)"); + return SASL_EXPIRED; + } + + /* find algorithm */ + text->alg = algorithm_options; + while (text->alg->name) { + if (!strcasecmp(text->alg->name, mda)) + break; + + text->alg++; + } + + if (!text->alg->name) { + params->utils->seterror(params->utils->conn, 0, + "unknown OTP algorithm '%s'", mda); + return SASL_FAIL; + } + + /* remake the secret with a timeout */ + result = make_secret(params->utils, text->alg->name, text->seq, + text->seed, text->otp, + text->timestamp + OTP_LOCK_TIMEOUT, &sec); + if (result != SASL_OK) { + SETERROR(params->utils, "error making OTP secret"); + return result; + } + + /* do the store */ + propctx = params->utils->prop_new(0); + if (!propctx) + result = SASL_FAIL; + if (result == SASL_OK) + result = params->utils->prop_request(propctx, store_request); + if (result == SASL_OK) + result = params->utils->prop_set(propctx, "cmusaslsecretOTP", + (char *) sec->data, sec->len); + if (result == SASL_OK) + result = params->utils->auxprop_store(params->utils->conn, + propctx, text->authid); + if (propctx) + params->utils->prop_dispose(&propctx); + + if (sec) params->utils->free(sec); + + if (result != SASL_OK) { + SETERROR(params->utils, "Error putting OTP secret"); + return result; + } + + text->locked = 1; + + result = _plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), OTP_CHALLENGE_MAX+1); + if (result != SASL_OK) return result; + + /* create challenge */ + sprintf(text->out_buf, "otp-%s %u %s ext", + text->alg->name, text->seq-1, text->seed); + + *serverout = text->out_buf; + *serveroutlen = (unsigned) strlen(text->out_buf); + + text->state = 2; + + return SASL_CONTINUE; +} + +static int +otp_server_mech_step2(server_context_t *text, + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout __attribute__((unused)), + unsigned *serveroutlen __attribute__((unused)), + sasl_out_params_t *oparams) +{ + char response[OTP_RESPONSE_MAX+1]; + int result; + sasl_secret_t *sec = NULL; + struct propctx *propctx = NULL; + const char *store_request[] = { "cmusaslsecretOTP", + NULL }; + + if (clientinlen > OTP_RESPONSE_MAX) { + SETERROR(params->utils, "OTP response too long"); + return SASL_BADPROT; + } + + /* we can't assume that the response is null-terminated */ + strncpy(response, clientin, clientinlen); + response[clientinlen] = '\0'; + + /* check timeout */ + if (time(0) > text->timestamp + OTP_LOCK_TIMEOUT) { + SETERROR(params->utils, "OTP: server timed out"); + return SASL_UNAVAIL; + } + + /* verify response */ + result = verify_response(text, params->utils, response); + if (result != SASL_OK) return result; + + /* make the new secret */ + result = make_secret(params->utils, text->alg->name, text->seq, + text->seed, text->otp, 0, &sec); + if (result != SASL_OK) { + SETERROR(params->utils, "error making OTP secret"); + } + + /* do the store */ + propctx = params->utils->prop_new(0); + if (!propctx) + result = SASL_FAIL; + if (result == SASL_OK) + result = params->utils->prop_request(propctx, store_request); + if (result == SASL_OK) + result = params->utils->prop_set(propctx, "cmusaslsecretOTP", + (char *) sec->data, sec->len); + if (result == SASL_OK) + result = params->utils->auxprop_store(params->utils->conn, + propctx, text->authid); + if (propctx) + params->utils->prop_dispose(&propctx); + + if (result) { + SETERROR(params->utils, "Error putting OTP secret"); + } + + text->locked = 0; + + if (sec) _plug_free_secret(params->utils, &sec); + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return result; +} + +static int otp_server_mech_step(void *conn_context, + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + server_context_t *text = (server_context_t *) conn_context; + + *serverout = NULL; + *serveroutlen = 0; + + switch (text->state) { + + case 1: + return otp_server_mech_step1(text, params, clientin, clientinlen, + serverout, serveroutlen, oparams); + + case 2: + return otp_server_mech_step2(text, params, clientin, clientinlen, + serverout, serveroutlen, oparams); + + default: + params->utils->log(NULL, SASL_LOG_ERR, + "Invalid OTP server step %d\n", text->state); + return SASL_FAIL; + } + + return SASL_FAIL; /* should never get here */ +} + +static void otp_server_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + server_context_t *text = (server_context_t *) conn_context; + sasl_secret_t *sec; + struct propctx *propctx = NULL; + const char *store_request[] = { "cmusaslsecretOTP", + NULL }; + int r; + + if (!text) return; + + /* if we created a challenge, but bailed before the verification of the + response, release the lock on the user key */ + if (text->locked && (time(0) < text->timestamp + OTP_LOCK_TIMEOUT)) { + r = make_secret(utils, text->alg->name, text->seq, + text->seed, text->otp, 0, &sec); + if (r != SASL_OK) { + SETERROR(utils, "error making OTP secret"); + if (sec) utils->free(sec); + sec = NULL; + } + + /* do the store */ + propctx = utils->prop_new(0); + if (!propctx) + r = SASL_FAIL; + if (!r) + r = utils->prop_request(propctx, store_request); + if (!r) + r = utils->prop_set(propctx, "cmusaslsecretOTP", + (sec ? (char *) sec->data : NULL), + (sec ? sec->len : 0)); + if (!r) + r = utils->auxprop_store(utils->conn, propctx, text->authid); + if (propctx) + utils->prop_dispose(&propctx); + + if (r) { + SETERROR(utils, "Error putting OTP secret"); + } + + if (sec) _plug_free_secret(utils, &sec); + } + + if (text->authid) _plug_free_string(utils, &(text->authid)); + if (text->realm) _plug_free_string(utils, &(text->realm)); + + if (text->out_buf) utils->free(text->out_buf); + + utils->free(text); +} + +static int otp_setpass(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *userstr, + const char *pass, unsigned passlen, + const char *oldpass __attribute__((unused)), + unsigned oldpasslen __attribute__((unused)), + unsigned flags) +{ + int r; + char *user = NULL; + char *user_only = NULL; + char *realm = NULL; + sasl_secret_t *sec; + struct propctx *propctx = NULL; + const char *store_request[] = { "cmusaslsecretOTP", + NULL }; + + /* Do we have a backend that can store properties? */ + if (!sparams->utils->auxprop_store || + sparams->utils->auxprop_store(NULL, NULL, NULL) != SASL_OK) { + SETERROR(sparams->utils, "OTP: auxprop backend can't store properties"); + return SASL_NOMECH; + } + + r = _plug_parseuser(sparams->utils, + &user_only, + &realm, + sparams->user_realm, + sparams->serverFQDN, + userstr); + if (r) { + SETERROR(sparams->utils, "OTP: Error parsing user"); + return r; + } + + r = _plug_make_fulluser(sparams->utils, &user, user_only, realm); + if (r) { + goto cleanup; + } + + if ((flags & SASL_SET_DISABLE) || pass == NULL) { + sec = NULL; + } else { + algorithm_option_t *algs; + const char *mda; + unsigned int len; + unsigned short randnum; + char seed[OTP_SEED_MAX+1]; + unsigned char otp[OTP_HASH_SIZE]; + + sparams->utils->getopt(sparams->utils->getopt_context, + "OTP", "otp_mda", &mda, &len); + if (!mda) mda = OTP_MDA_DEFAULT; + + algs = algorithm_options; + while (algs->name) { + if (!strcasecmp(algs->name, mda) || + !strcasecmp(algs->evp_name, mda)) + break; + + algs++; + } + + if (!algs->name) { + sparams->utils->seterror(sparams->utils->conn, 0, + "unknown OTP algorithm '%s'", mda); + r = SASL_FAIL; + goto cleanup; + } + + sparams->utils->rand(sparams->utils->rpool, + (char*) &randnum, sizeof(randnum)); + sprintf(seed, "%.2s%04u", sparams->serverFQDN, (randnum % 9999) + 1); + + r = generate_otp(sparams->utils, algs, OTP_SEQUENCE_DEFAULT, + seed, (unsigned char *) pass, passlen, otp); + if (r != SASL_OK) { + /* generate_otp() takes care of error message */ + goto cleanup; + } + + r = make_secret(sparams->utils, algs->name, OTP_SEQUENCE_DEFAULT, + seed, otp, 0, &sec); + if (r != SASL_OK) { + SETERROR(sparams->utils, "error making OTP secret"); + goto cleanup; + } + } + + /* do the store */ + propctx = sparams->utils->prop_new(0); + if (!propctx) + r = SASL_FAIL; + if (!r) + r = sparams->utils->prop_request(propctx, store_request); + if (!r) + r = sparams->utils->prop_set(propctx, "cmusaslsecretOTP", + (sec ? (char *) sec->data : NULL), + (sec ? sec->len : 0)); + if (!r) + r = sparams->utils->auxprop_store(sparams->utils->conn, propctx, user); + if (propctx) + sparams->utils->prop_dispose(&propctx); + + if (r) { + SETERROR(sparams->utils, "Error putting OTP secret"); + goto cleanup; + } + + sparams->utils->log(NULL, SASL_LOG_DEBUG, "Setpass for OTP successful\n"); + + cleanup: + + if (user) _plug_free_string(sparams->utils, &user); + if (user_only) _plug_free_string(sparams->utils, &user_only); + if (realm) _plug_free_string(sparams->utils, &realm); + if (sec) _plug_free_secret(sparams->utils, &sec); + + return r; +} + +static int otp_mech_avail(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + void **conn_context __attribute__((unused))) +{ + /* Do we have a backend that can store properties? */ + if (!sparams->utils->auxprop_store || + sparams->utils->auxprop_store(NULL, NULL, NULL) != SASL_OK) { + sparams->utils->log(NULL, + SASL_LOG_DEBUG, + "OTP: auxprop backend can't store properties"); + return SASL_NOMECH; + } + + return SASL_OK; +} + +static sasl_server_plug_t otp_server_plugins[] = +{ + { + "OTP", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_FORWARD_SECRECY, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* glob_context */ + &otp_server_mech_new, /* mech_new */ + &otp_server_mech_step, /* mech_step */ + &otp_server_mech_dispose, /* mech_dispose */ + &otp_common_mech_free, /* mech_free */ + &otp_setpass, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + &otp_mech_avail, /* mech avail */ + NULL /* spare */ + } +}; +#endif /* HAVE_OPIE */ + +int otp_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { + SETERROR(utils, "OTP version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = otp_server_plugins; + *plugcount = 1; + + /* Add all digests */ + OpenSSL_add_all_digests(); + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + int state; + + sasl_secret_t *password; + unsigned int free_password; /* set if we need to free password */ + + const char *otpassword; + + char *out_buf; + unsigned out_buf_len; + + char challenge[OTP_CHALLENGE_MAX+1]; +} client_context_t; + +static int otp_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + client_context_t *text; + + /* holds state are in */ + text = params->utils->malloc(sizeof(client_context_t)); + if (text == NULL) { + MEMERROR( params->utils ); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(client_context_t)); + + text->state = 1; + + *conn_context = text; + + return SASL_OK; +} + +static int otp_client_mech_step1(client_context_t *text, + sasl_client_params_t *params, + const char *serverin __attribute__((unused)), + unsigned serverinlen __attribute__((unused)), + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + const char *user = NULL, *authid = NULL; + int user_result = SASL_OK; + int auth_result = SASL_OK; + int pass_result = SASL_OK; + sasl_chalprompt_t *echo_cb; + void *echo_context; + int result; + + /* check if sec layer strong enough */ + if (params->props.min_ssf > params->external_ssf) { + SETERROR( params->utils, "SSF requested of OTP plugin"); + return SASL_TOOWEAK; + } + + /* try to get the authid */ + if (oparams->authid == NULL) { + auth_result = _plug_get_authid(params->utils, &authid, prompt_need); + + if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) + return auth_result; + } + + /* try to get the userid */ + if (oparams->user == NULL) { + user_result = _plug_get_userid(params->utils, &user, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) + return user_result; + } + + /* try to get the secret pass-phrase if we don't have a chalprompt */ + if ((params->utils->getcallback(params->utils->conn, SASL_CB_ECHOPROMPT, + (sasl_callback_ft *)&echo_cb, &echo_context) == SASL_FAIL) && + (text->password == NULL)) { + pass_result = _plug_get_password(params->utils, &text->password, + &text->free_password, prompt_need); + + if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) + return pass_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) || + (pass_result == SASL_INTERACT)) { + /* make the prompt list */ + result = + _plug_make_prompts(params->utils, prompt_need, + user_result == SASL_INTERACT ? + "Please enter your authorization name" : NULL, + NULL, + auth_result == SASL_INTERACT ? + "Please enter your authentication name" : NULL, + NULL, + pass_result == SASL_INTERACT ? + "Please enter your secret pass-phrase" : NULL, + NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (result != SASL_OK) return result; + + return SASL_INTERACT; + } + + if (!user || !*user) { + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + } + else { + result = params->canon_user(params->utils->conn, user, 0, + SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID, oparams); + } + if (result != SASL_OK) return result; + + /* send authorized id NUL authentication id */ + *clientoutlen = oparams->ulen + 1 + oparams->alen; + + /* remember the extra NUL on the end for stupid clients */ + result = _plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), *clientoutlen + 1); + if (result != SASL_OK) return result; + + memset(text->out_buf, 0, *clientoutlen + 1); + memcpy(text->out_buf, oparams->user, oparams->ulen); + memcpy(text->out_buf+oparams->ulen+1, oparams->authid, oparams->alen); + *clientout = text->out_buf; + + text->state = 2; + + return SASL_CONTINUE; +} + +static int otp_client_mech_step2(client_context_t *text, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + int echo_result = SASL_OK; + int result; + + if (serverinlen > OTP_CHALLENGE_MAX) { + SETERROR(params->utils, "OTP challenge too long"); + return SASL_BADPROT; + } + + /* we can't assume that challenge is null-terminated */ + strncpy(text->challenge, serverin, serverinlen); + text->challenge[serverinlen] = '\0'; + + /* try to get the one-time password if we don't have the secret */ + if ((text->password == NULL) && (text->otpassword == NULL)) { + echo_result = _plug_challenge_prompt(params->utils, + SASL_CB_ECHOPROMPT, + text->challenge, + "Please enter your one-time password", + &text->otpassword, + prompt_need); + + if ((echo_result != SASL_OK) && (echo_result != SASL_INTERACT)) + return echo_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if (echo_result == SASL_INTERACT) { + /* make the prompt list */ + result = + _plug_make_prompts(params->utils, + prompt_need, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + text->challenge, + "Please enter your one-time password", + NULL, + NULL, + NULL, + NULL); + if (result != SASL_OK) return result; + + return SASL_INTERACT; + } + + /* the application provided us with a one-time password so use it */ + if (text->otpassword) { + *clientout = text->otpassword; + *clientoutlen = (unsigned) strlen(text->otpassword); + } + /* generate our own response using the user's secret pass-phrase */ + else { + algorithm_option_t *alg; + unsigned seq; + char seed[OTP_SEED_MAX+1]; + unsigned char otp[OTP_HASH_SIZE]; + int init_done = 0; + + /* parse challenge */ + result = parse_challenge(params->utils, + text->challenge, + &alg, + &seq, + seed, + 0); + if (result != SASL_OK) return result; + + if (!text->password) { + PARAMERROR(params->utils); + return SASL_BADPARAM; + } + + if (seq < 1) { + SETERROR(params->utils, "OTP has expired (sequence < 1)"); + return SASL_EXPIRED; + } + + /* generate otp */ + result = generate_otp(params->utils, alg, seq, seed, + text->password->data, text->password->len, otp); + if (result != SASL_OK) return result; + + result = _plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), OTP_RESPONSE_MAX+1); + if (result != SASL_OK) return result; + + if (seq < OTP_SEQUENCE_REINIT) { + unsigned short randnum; + char new_seed[OTP_SEED_MAX+1]; + unsigned char new_otp[OTP_HASH_SIZE]; + + /* try to reinitialize */ + + /* make sure we have a different seed */ + do { + params->utils->rand(params->utils->rpool, + (char*) &randnum, sizeof(randnum)); + sprintf(new_seed, "%.2s%04u", params->serverFQDN, + (randnum % 9999) + 1); + } while (!strcasecmp(seed, new_seed)); + + result = generate_otp(params->utils, alg, OTP_SEQUENCE_DEFAULT, + new_seed, text->password->data, text->password->len, new_otp); + + if (result == SASL_OK) { + /* create an init-hex response */ + strcpy(text->out_buf, OTP_INIT_HEX_TYPE); + bin2hex(otp, OTP_HASH_SIZE, + text->out_buf+strlen(text->out_buf)); + sprintf(text->out_buf+strlen(text->out_buf), ":%s %u %s:", + alg->name, OTP_SEQUENCE_DEFAULT, new_seed); + bin2hex(new_otp, OTP_HASH_SIZE, + text->out_buf+strlen(text->out_buf)); + init_done = 1; + } + else { + /* just do a regular response */ + } + } + + if (!init_done) { + /* created hex response */ + strcpy(text->out_buf, OTP_HEX_TYPE); + bin2hex(otp, OTP_HASH_SIZE, text->out_buf+strlen(text->out_buf)); + } + + *clientout = text->out_buf; + *clientoutlen = (unsigned) strlen(text->out_buf); + } + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static int otp_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + client_context_t *text = (client_context_t *) conn_context; + + *clientout = NULL; + *clientoutlen = 0; + + switch (text->state) { + + case 1: + return otp_client_mech_step1(text, params, serverin, serverinlen, + prompt_need, clientout, clientoutlen, + oparams); + + case 2: + return otp_client_mech_step2(text, params, serverin, serverinlen, + prompt_need, clientout, clientoutlen, + oparams); + + default: + params->utils->log(NULL, SASL_LOG_ERR, + "Invalid OTP client step %d\n", text->state); + return SASL_FAIL; + } + + return SASL_FAIL; /* should never get here */ +} + +static void otp_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + client_context_t *text = (client_context_t *) conn_context; + + if (!text) return; + + if (text->free_password) _plug_free_secret(utils, &(text->password)); + + if (text->out_buf) utils->free(text->out_buf); + + utils->free(text); +} + +static sasl_client_plug_t otp_client_plugins[] = +{ + { + "OTP", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_FORWARD_SECRECY, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* required_prompts */ + NULL, /* glob_context */ + &otp_client_mech_new, /* mech_new */ + &otp_client_mech_step, /* mech_step */ + &otp_client_mech_dispose, /* mech_dispose */ + &otp_common_mech_free, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int otp_client_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR(utils, "OTP version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = otp_client_plugins; + *plugcount = 1; + + /* Add all digests */ + OpenSSL_add_all_digests(); + + return SASL_OK; +} diff --git a/contrib/libs/sasl/plugins/otp.h b/contrib/libs/sasl/plugins/otp.h new file mode 100644 index 0000000000..06ad32c5c6 --- /dev/null +++ b/contrib/libs/sasl/plugins/otp.h @@ -0,0 +1,311 @@ +/* OTP SASL plugin + * Ken Murchison + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OTP_H_ +#define _OTP_H_ + +/* Standard dictionary from RFC2289 */ +#define OTP_STD_DICT_SIZE 2048 +#define OTP_4LETTER_OFFSET 571 + +static const char *otp_std_dict[OTP_STD_DICT_SIZE] = +{ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", +"AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", +"AN", "ANA", "AND", "ANN", "ANT", "ANY", "APE", "APS", +"APT", "ARC", "ARE", "ARK", "ARM", "ART", "AS", "ASH", +"ASK", "AT", "ATE", "AUG", "AUK", "AVE", "AWE", "AWK", +"AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", +"BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", +"BEN", "BET", "BEY", "BIB", "BID", "BIG", "BIN", "BIT", +"BOB", "BOG", "BON", "BOO", "BOP", "BOW", "BOY", "BUB", +"BUD", "BUG", "BUM", "BUN", "BUS", "BUT", "BUY", "BY", +"BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", +"CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", +"COW", "COY", "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", +"DAB", "DAD", "DAM", "DAN", "DAR", "DAY", "DEE", "DEL", +"DEN", "DES", "DEW", "DID", "DIE", "DIG", "DIN", "DIP", +"DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", +"DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", +"EGG", "EGO", "ELI", "ELK", "ELM", "ELY", "EM", "END", +"EST", "ETC", "EVA", "EVE", "EWE", "EYE", "FAD", "FAN", +"FAR", "FAT", "FAY", "FED", "FEE", "FEW", "FIB", "FIG", +"FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", +"FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", +"GAM", "GAP", "GAS", "GAY", "GEE", "GEL", "GEM", "GET", +"GIG", "GIL", "GIN", "GO", "GOT", "GUM", "GUN", "GUS", +"GUT", "GUY", "GYM", "GYP", "HA", "HAD", "HAL", "HAM", +"HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", +"HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", +"HIS", "HIT", "HO", "HOB", "HOC", "HOE", "HOG", "HOP", +"HOT", "HOW", "HUB", "HUE", "HUG", "HUH", "HUM", "HUT", +"I", "ICY", "IDA", "IF", "IKE", "ILL", "INK", "INN", +"IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", +"ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", +"JAY", "JET", "JIG", "JIM", "JO", "JOB", "JOE", "JOG", +"JOT", "JOY", "JUG", "JUT", "KAY", "KEG", "KEN", "KEY", +"KID", "KIM", "KIN", "KIT", "LA", "LAB", "LAC", "LAD", +"LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", +"LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", +"LIP", "LIT", "LO", "LOB", "LOG", "LOP", "LOS", "LOT", +"LOU", "LOW", "LOY", "LUG", "LYE", "MA", "MAC", "MAD", +"MAE", "MAN", "MAO", "MAP", "MAT", "MAW", "MAY", "ME", +"MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", +"MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", +"MUD", "MUG", "MUM", "MY", "NAB", "NAG", "NAN", "NAP", +"NAT", "NAY", "NE", "NED", "NEE", "NET", "NEW", "NIB", +"NIL", "NIP", "NIT", "NO", "NOB", "NOD", "NON", "NOR", +"NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", +"OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", +"OH", "OIL", "OK", "OLD", "ON", "ONE", "OR", "ORB", +"ORE", "ORR", "OS", "OTT", "OUR", "OUT", "OVA", "OW", +"OWE", "OWL", "OWN", "OX", "PA", "PAD", "PAL", "PAM", +"PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", +"PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", +"PIN", "PIT", "PLY", "PO", "POD", "POE", "POP", "POT", +"POW", "PRO", "PRY", "PUB", "PUG", "PUN", "PUP", "PUT", +"QUO", "RAG", "RAM", "RAN", "RAP", "RAT", "RAW", "RAY", +"REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", +"RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", +"ROY", "RUB", "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", +"SAD", "SAG", "SAL", "SAM", "SAN", "SAP", "SAT", "SAW", +"SAY", "SEA", "SEC", "SEE", "SEN", "SET", "SEW", "SHE", +"SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", +"SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", +"SPA", "SPY", "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", +"TAB", "TAD", "TAG", "TAN", "TAP", "TAR", "TEA", "TED", +"TEE", "TEN", "THE", "THY", "TIC", "TIE", "TIM", "TIN", +"TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", +"TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", +"UN", "UP", "US", "USE", "VAN", "VAT", "VET", "VIE", +"WAD", "WAG", "WAR", "WAS", "WAY", "WE", "WEB", "WED", +"WEE", "WET", "WHO", "WHY", "WIN", "WIT", "WOK", "WON", +"WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", +"YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", +"ABUT", "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", +"ADDS", "ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", +"AIDE", "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", +"ALIA", "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", +"AMEN", "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", +"ANEW", "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", +"AREA", "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", +"ATOM", "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", +"AVOW", "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", +"BAIL", "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", +"BALM", "BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", +"BARK", "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", +"BATH", "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", +"BEAT", "BEAU", "BECK", "BEEF", "BEEN", "BEER", "BEET", "BELA", +"BELL", "BELT", "BEND", "BENT", "BERG", "BERN", "BERT", "BESS", +"BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", "BIEN", "BILE", +"BILK", "BILL", "BIND", "BING", "BIRD", "BITE", "BITS", "BLAB", +"BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", "BLOW", "BLUE", +"BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", "BODE", "BODY", +"BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", "BOMB", "BONA", +"BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", "BOOM", "BOON", +"BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", "BOTH", "BOUT", +"BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", "BRAY", "BRED", +"BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", "BUFF", "BULB", +"BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", "BURL", "BURN", +"BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", "BUSY", "BYTE", +"CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", "CALL", "CALM", +"CAME", "CANE", "CANT", "CARD", "CARE", "CARL", "CARR", "CART", +"CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", "CELL", "CENT", +"CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", "CHEN", "CHEW", +"CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", "CHUM", "CITE", +"CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", "CLOD", "CLOG", +"CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", "COCK", "COCO", +"CODA", "CODE", "CODY", "COED", "COIL", "COIN", "COKE", "COLA", +"COLD", "COLT", "COMA", "COMB", "COME", "COOK", "COOL", "COON", +"COOT", "CORD", "CORE", "CORK", "CORN", "COST", "COVE", "COWL", +"CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB", "CROW", "CRUD", +"CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", "CURB", "CURD", +"CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", "DAME", "DANA", +"DANE", "DANG", "DANK", "DARE", "DARK", "DARN", "DART", "DASH", +"DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", "DEAD", "DEAF", +"DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", "DEEM", "DEER", +"DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", "DIAL", "DICE", +"DIED", "DIET", "DIME", "DINE", "DING", "DINT", "DIRE", "DIRT", +"DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", "DOLE", "DOLL", +"DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA", "DOSE", "DOTE", +"DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", "DRAM", "DRAW", +"DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", "DUCT", "DUEL", +"DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", "DUSK", "DUST", +"DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", "EASY", "EBEN", +"ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", "EDNA", "EGAN", +"ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", "EMMA", "ENDS", +"ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", "FACE", "FACT", +"FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", "FAME", "FANG", +"FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT", "FEED", "FEEL", +"FEET", "FELL", "FELT", "FEND", "FERN", "FEST", "FEUD", "FIEF", +"FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", "FINK", "FIRE", +"FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE", "FLAG", "FLAK", +"FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", "FLIT", "FLOC", +"FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", "FOGY", "FOIL", +"FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", "FOOT", "FORD", +"FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", "FOUR", "FOWL", +"FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", "FROG", "FROM", +"FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", "FUSE", "FUSS", +"GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA", "GALE", "GALL", +"GALT", "GAME", "GANG", "GARB", "GARY", "GASH", "GATE", "GAUL", +"GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE", "GENT", "GERM", +"GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", "GINA", "GIRD", +"GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", "GLIB", "GLOB", +"GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", "GOAL", "GOAT", +"GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", "GOOD", "GOOF", +"GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", "GRAD", "GRAY", +"GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", "GRIT", "GROW", +"GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH", "GUST", "GWEN", +"GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", "HALE", "HALF", +"HALL", "HALO", "HALT", "HAND", "HANG", "HANK", "HANS", "HARD", +"HARK", "HARM", "HART", "HASH", "HAST", "HATE", "HATH", "HAUL", +"HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", "HEAT", "HEBE", +"HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", "HELM", "HERB", +"HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", "HICK", "HIDE", +"HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", "HIRE", "HISS", +"HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", "HOLM", "HOLT", +"HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK", "HOOT", "HORN", +"HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", "HOYT", "HUCK", +"HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", "HULL", "HUNK", +"HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", "HYMN", "IBIS", +"ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", "INTO", "IONS", +"IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", "ITCH", "ITEM", +"IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", "JAVA", "JEAN", +"JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", "JILT", "JIVE", +"JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", "JOIN", "JOKE", +"JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", "JUJU", "JUKE", +"JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", "JUTE", "KAHN", +"KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", "KEEN", "KENO", +"KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", "KIND", "KING", +"KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", "KNIT", "KNOB", +"KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", "KURT", "KYLE", +"LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", "LAIR", "LAKE", +"LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", "LARK", "LASS", +"LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS", "LAYS", "LEAD", +"LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", "LEFT", "LEND", +"LENS", "LENT", "LEON", "LESK", "LESS", "LEST", "LETS", "LIAR", +"LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", "LIFE", "LIFT", +"LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", "LIME", "LIND", +"LINE", "LINK", "LINT", "LION", "LISA", "LIST", "LIVE", "LOAD", +"LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", "LOIS", "LOLA", +"LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", "LORE", "LOSE", +"LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", "LUCY", "LUGE", +"LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE", "LURK", "LUSH", +"LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", "MADE", "MAGI", +"MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", "MALL", "MALT", +"MANA", "MANN", "MANY", "MARC", "MARE", "MARK", "MARS", "MART", +"MARY", "MASH", "MASK", "MASS", "MAST", "MATE", "MATH", "MAUL", +"MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", "MEET", "MELD", +"MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", "MESS", "MICE", +"MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", "MIMI", "MIND", +"MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", "MIST", "MITE", +"MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD", "MOLE", "MOLL", +"MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", "MOOR", "MOOT", +"MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", "MOVE", "MUCH", +"MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK", "MUSH", "MUST", +"MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", "NAIR", "NAME", +"NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", "NEAT", "NECK", +"NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", "NEST", "NEWS", +"NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", "NINE", "NOAH", +"NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", "NORM", "NOSE", +"NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", "OATH", "OBEY", +"OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY", "OLAF", "OLDY", +"OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", "ONES", "ONLY", +"ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", "OTTO", "OUCH", +"OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", "OWNS", "QUAD", +"QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", "RAGE", "RAID", +"RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", "RASH", "RATE", +"RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", "RECK", "REED", +"REEF", "REEK", "REEL", "REID", "REIN", "RENA", "REND", "RENT", +"REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", "RILL", "RIME", +"RING", "RINK", "RISE", "RISK", "RITE", "ROAD", "ROAM", "ROAR", +"ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", "ROOD", "ROOF", +"ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", "ROSY", "ROTH", +"ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", "RUDE", "RUDY", +"RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", "RUSH", "RUSK", +"RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", "SAID", "SAIL", +"SALE", "SALK", "SALT", "SAME", "SAND", "SANE", "SANG", "SANK", +"SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", "SCAT", "SCOT", +"SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", "SEEM", "SEEN", +"SEES", "SELF", "SELL", "SEND", "SENT", "SETS", "SEWN", "SHAG", +"SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", "SHOD", "SHOE", +"SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", "SIFT", "SIGH", +"SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", "SING", "SINK", +"SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", "SKID", "SKIM", +"SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", "SLED", "SLEW", +"SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", "SLOW", "SLUG", +"SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", "SNOW", "SNUB", +"SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", "SOFT", "SOIL", +"SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", "SORT", "SOUL", +"SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", "STAY", "STEM", +"STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", "SUDS", "SUIT", +"SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", "SWAB", "SWAG", +"SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", "TACK", "TACT", +"TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", "TASK", "TATE", +"TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", "TEEN", "TEET", +"TELL", "TEND", "TENT", "TERM", "TERN", "TESS", "TEST", "THAN", +"THAT", "THEE", "THEM", "THEN", "THEY", "THIN", "THIS", "THUD", +"THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", "TILE", "TILL", +"TILT", "TIME", "TINA", "TINE", "TINT", "TINY", "TIRE", "TOAD", +"TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", "TONY", "TOOK", +"TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", "TOUT", "TOWN", +"TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", "TRIM", "TRIO", +"TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", "TUCK", "TUFT", +"TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", "TWIG", "TWIN", +"TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", "USES", "UTAH", +"VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST", "VEAL", "VEDA", +"VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", "VETO", "VICE", +"VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", "WACK", "WADE", +"WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", "WALL", "WALT", +"WAND", "WANE", "WANG", "WANT", "WARD", "WARM", "WARN", "WART", +"WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", "WAYS", "WEAK", +"WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", "WELD", "WELL", +"WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", "WHAT", "WHEE", +"WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", "WILD", "WILL", +"WIND", "WINE", "WING", "WINK", "WINO", "WIRE", "WISE", "WISH", +"WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", "WORE", "WORK", +"WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", "YANG", "YANK", +"YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR", "YELL", "YOGA", +"YOKE" }; + +#endif /* _OTP_H_ */ diff --git a/contrib/libs/sasl/plugins/plain.c b/contrib/libs/sasl/plugins/plain.c new file mode 100644 index 0000000000..eb48687032 --- /dev/null +++ b/contrib/libs/sasl/plugins/plain.c @@ -0,0 +1,489 @@ +/* Plain SASL plugin + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <sasl.h> +#include <saslplug.h> + +#include "plugin_common.h" + +#ifdef macintosh +#error #include <sasl_plain_plugin_decl.h> +#endif + +/***************************** Common Section *****************************/ + +/***************************** Server Section *****************************/ + +static int plain_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + /* holds state are in */ + if (!conn_context) { + PARAMERROR( sparams->utils ); + return SASL_BADPARAM; + } + + *conn_context = NULL; + + return SASL_OK; +} + +static int plain_server_mech_step(void *conn_context __attribute__((unused)), + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + const char *author; + const char *authen; + const char *password; + unsigned password_len; + unsigned lup = 0; + int result; + char *passcopy; + unsigned canon_flags = 0; + + *serverout = NULL; + *serveroutlen = 0; + + /* should have received author-id NUL authen-id NUL password */ + + /* get author */ + author = clientin; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + if (lup >= clientinlen) { + SETERROR(params->utils, "Can only find author (no password)"); + return SASL_BADPROT; + } + + /* get authen */ + ++lup; + authen = clientin + lup; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + if (lup >= clientinlen) { + params->utils->seterror(params->utils->conn, 0, + "Can only find author/en (no password)"); + return SASL_BADPROT; + } + + /* get password */ + lup++; + password = clientin + lup; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + password_len = (unsigned) (clientin + lup - password); + + if (lup != clientinlen) { + SETERROR(params->utils, + "Got more data than we were expecting in the PLAIN plugin\n"); + return SASL_BADPROT; + } + + /* this kinda sucks. we need password to be null terminated + but we can't assume there is an allocated byte at the end + of password so we have to copy it */ + passcopy = params->utils->malloc(password_len + 1); + if (passcopy == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + strncpy(passcopy, password, password_len); + passcopy[password_len] = '\0'; + + /* Canonicalize userid first, so that password verification is only + * against the canonical id */ + if (!author || !*author) { + author = authen; + canon_flags = SASL_CU_AUTHZID; + } else if (strcmp(author, authen) == 0) { + /* While this isn't going to find out that <user> and <user>@<defaultdomain> + are the same thing, this is good enough for many cases */ + canon_flags = SASL_CU_AUTHZID; + } + + result = params->canon_user(params->utils->conn, + authen, + 0, + SASL_CU_AUTHID | canon_flags | SASL_CU_EXTERNALLY_VERIFIED, + oparams); + if (result != SASL_OK) { + _plug_free_string(params->utils, &passcopy); + return result; + } + + /* verify password (and possibly fetch both authentication and + authorization identity related properties) - return SASL_OK + on success */ + result = params->utils->checkpass(params->utils->conn, + oparams->authid, + oparams->alen, + passcopy, + password_len); + + _plug_free_string(params->utils, &passcopy); + + if (result != SASL_OK) { + params->utils->seterror(params->utils->conn, 0, + "Password verification failed"); + return result; + } + + /* Canonicalize and store the authorization ID */ + /* We need to do this after calling verify_user just in case verify_user + * needed to get auxprops itself */ + if (canon_flags == 0) { + const struct propval *pr; + int i; + + pr = params->utils->prop_get(params->propctx); + if (!pr) { + return SASL_FAIL; + } + + /* params->utils->checkpass() might have fetched authorization identity related properties + for the wrong user name. Free these values. */ + for (i = 0; pr[i].name; i++) { + if (pr[i].name[0] == '*') { + continue; + } + + if (pr[i].values) { + params->utils->prop_erase(params->propctx, pr[i].name); + } + } + + result = params->canon_user(params->utils->conn, + author, + 0, + SASL_CU_AUTHZID, + oparams); + if (result != SASL_OK) { + return result; + } + } + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static sasl_server_plug_t plain_server_plugins[] = +{ + { + "PLAIN", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOANONYMOUS + | SASL_SEC_PASS_CREDENTIALS, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* glob_context */ + &plain_server_mech_new, /* mech_new */ + &plain_server_mech_step, /* mech_step */ + NULL, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech_avail */ + NULL /* spare */ + } +}; + +int plain_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { + SETERROR(utils, "PLAIN version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = plain_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + char *out_buf; + unsigned out_buf_len; +} client_context_t; + +static int plain_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + client_context_t *text; + + /* holds state are in */ + text = params->utils->malloc(sizeof(client_context_t)); + if (text == NULL) { + MEMERROR( params->utils ); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(client_context_t)); + + *conn_context = text; + + return SASL_OK; +} + +static int plain_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin __attribute__((unused)), + unsigned serverinlen __attribute__((unused)), + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + client_context_t *text = (client_context_t *) conn_context; + const char *user = NULL, *authid = NULL; + sasl_secret_t *password = NULL; + unsigned int free_password = 0; /* set if we need to free password */ + int user_result = SASL_OK; + int auth_result = SASL_OK; + int pass_result = SASL_OK; + int result; + char *p; + + *clientout = NULL; + *clientoutlen = 0; + + /* doesn't really matter how the server responds */ + + /* check if sec layer strong enough */ + if (params->props.min_ssf > params->external_ssf) { + SETERROR( params->utils, "SSF requested of PLAIN plugin"); + return SASL_TOOWEAK; + } + + /* try to get the authid */ + if (oparams->authid == NULL) { + auth_result = _plug_get_authid(params->utils, &authid, prompt_need); + + if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) + return auth_result; + } + + /* try to get the userid */ + if (oparams->user == NULL) { + user_result = _plug_get_userid(params->utils, &user, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) + return user_result; + } + + /* try to get the password */ + if (password == NULL) { + pass_result = _plug_get_password(params->utils, &password, + &free_password, prompt_need); + + if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) + return pass_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) || + (pass_result == SASL_INTERACT)) { + /* make the prompt list */ + result = + _plug_make_prompts(params->utils, prompt_need, + user_result == SASL_INTERACT ? + "Please enter your authorization name" : NULL, + NULL, + auth_result == SASL_INTERACT ? + "Please enter your authentication name" : NULL, + NULL, + pass_result == SASL_INTERACT ? + "Please enter your password" : NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (result != SASL_OK) goto cleanup; + + return SASL_INTERACT; + } + + if (!password) { + PARAMERROR(params->utils); + return SASL_BADPARAM; + } + + if (!user || !*user) { + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + } + else { + result = params->canon_user(params->utils->conn, user, 0, + SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) goto cleanup; + + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID, oparams); + } + if (result != SASL_OK) goto cleanup; + + /* send authorized id NUL authentication id NUL password */ + *clientoutlen = ((user && *user ? oparams->ulen : 0) + + 1 + oparams->alen + + 1 + password->len); + + /* remember the extra NUL on the end for stupid clients */ + result = _plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), *clientoutlen + 1); + if (result != SASL_OK) goto cleanup; + + memset(text->out_buf, 0, *clientoutlen + 1); + p = text->out_buf; + if (user && *user) { + memcpy(p, oparams->user, oparams->ulen); + p += oparams->ulen; + } + memcpy(++p, oparams->authid, oparams->alen); + p += oparams->alen; + memcpy(++p, password->data, password->len); + + *clientout = text->out_buf; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + result = SASL_OK; + + cleanup: + /* free sensitive info */ + if (free_password) _plug_free_secret(params->utils, &password); + + return result; +} + +static void plain_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + client_context_t *text = (client_context_t *) conn_context; + + if (!text) return; + + if (text->out_buf) utils->free(text->out_buf); + + utils->free(text); +} + +static sasl_client_plug_t plain_client_plugins[] = +{ + { + "PLAIN", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOANONYMOUS + | SASL_SEC_PASS_CREDENTIALS, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* required_prompts */ + NULL, /* glob_context */ + &plain_client_mech_new, /* mech_new */ + &plain_client_mech_step, /* mech_step */ + &plain_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int plain_client_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR(utils, "PLAIN version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = plain_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/plugins/sasldb.c b/contrib/libs/sasl/plugins/sasldb.c new file mode 100644 index 0000000000..f4e5412b17 --- /dev/null +++ b/contrib/libs/sasl/plugins/sasldb.c @@ -0,0 +1,317 @@ +/* SASL server API implementation + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> + +/* sasldb stuff */ + +#include <stdio.h> + +#include "sasl.h" +#include "saslutil.h" +#include "saslplug.h" +#include "../sasldb/sasldb.h" + +#include "plugin_common.h" + +static int sasldb_auxprop_lookup(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + unsigned flags, + const char *user, + unsigned ulen) +{ + char *userid = NULL; + char *realm = NULL; + const char *user_realm = NULL; + int ret; + const struct propval *to_fetch, *cur; + char value[8192]; + size_t value_len; + char *user_buf; + int verify_against_hashed_password; + int saw_user_password = 0; + + if (!sparams || !user) return SASL_BADPARAM; + + user_buf = sparams->utils->malloc(ulen + 1); + if(!user_buf) { + ret = SASL_NOMEM; + goto done; + } + + memcpy(user_buf, user, ulen); + user_buf[ulen] = '\0'; + + if(sparams->user_realm) { + user_realm = sparams->user_realm; + } else { + user_realm = sparams->serverFQDN; + } + + ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm, + sparams->serverFQDN, user_buf); + if(ret != SASL_OK) goto done; + + to_fetch = sparams->utils->prop_get(sparams->propctx); + if (!to_fetch) { + ret = SASL_NOMEM; + goto done; + } + + verify_against_hashed_password = flags & SASL_AUXPROP_VERIFY_AGAINST_HASH; + + /* Use a fake value to signal that we have no property to lookup */ + ret = SASL_CONTINUE; + for(cur = to_fetch; cur->name; cur++) { + int cur_ret; + const char *realname = cur->name; + + /* Only look up properties that apply to this lookup! */ + if(cur->name[0] == '*' && (flags & SASL_AUXPROP_AUTHZID)) continue; + if(!(flags & SASL_AUXPROP_AUTHZID)) { + if(cur->name[0] != '*') continue; + else realname = cur->name + 1; + } + + /* If it's there already, we want to see if it needs to be + * overridden. userPassword is a special case, because it's value + is always present if SASL_AUXPROP_VERIFY_AGAINST_HASH is specified. + When SASL_AUXPROP_VERIFY_AGAINST_HASH is set, we just clear userPassword. */ + if (cur->values && !(flags & SASL_AUXPROP_OVERRIDE) && + (verify_against_hashed_password == 0 || + strcasecmp(realname, SASL_AUX_PASSWORD_PROP) != 0)) { + continue; + } else if (cur->values) { + sparams->utils->prop_erase(sparams->propctx, cur->name); + } + + if (strcasecmp(realname, SASL_AUX_PASSWORD_PROP) == 0) { + saw_user_password = 1; + } + + cur_ret = _sasldb_getdata(sparams->utils, + sparams->utils->conn, userid, realm, + realname, value, sizeof(value), &value_len); + + /* Assumption: cur_ret is never SASL_CONTINUE */ + + /* If this is the first property we've tried to fetch ==> + always set the global error code. + If we had SASL_NOUSER ==> any other error code overrides it + (including SASL_NOUSER). */ + if (ret == SASL_CONTINUE || ret == SASL_NOUSER) { + ret = cur_ret; + } else if (ret == SASL_OK) { + /* Any error code other than SASL_NOUSER overrides SASL_OK. + (And SASL_OK overrides SASL_OK as well) */ + if (cur_ret != SASL_NOUSER) { + ret = cur_ret; + } + } + /* Any other global error code is left as is */ + + if (cur_ret != SASL_OK) { + if (cur_ret != SASL_NOUSER) { + /* No point in continuing if we hit any serious error */ + break; + } + /* We didn't find it, leave it as not found */ + continue; + } + + sparams->utils->prop_set(sparams->propctx, cur->name, + value, (unsigned) value_len); + } + + /* [Keep in sync with LDAPDB, SQL] + If ret is SASL_CONTINUE, it means that no properties were requested + (or maybe some were requested, but they already have values and + SASL_AUXPROP_OVERRIDE flag is not set). + Always return SASL_OK in this case. */ + if (ret == SASL_CONTINUE) { + ret = SASL_OK; + } + + if (flags & SASL_AUXPROP_AUTHZID) { + /* This is a lie, but the caller can't handle + when we return SASL_NOUSER for authorization identity lookup. */ + if (ret == SASL_NOUSER) { + ret = SASL_OK; + } + } else { + if (ret == SASL_NOUSER && saw_user_password == 0) { + /* Verify user existence by checking presence of + the userPassword attribute */ + ret = _sasldb_getdata(sparams->utils, + sparams->utils->conn, + userid, + realm, + SASL_AUX_PASSWORD_PROP, + value, + sizeof(value), + &value_len); + } + } + + done: + if (userid) sparams->utils->free(userid); + if (realm) sparams->utils->free(realm); + if (user_buf) sparams->utils->free(user_buf); + + return ret; +} + +static int sasldb_auxprop_store(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + struct propctx *ctx, + const char *user, + unsigned ulen) +{ + char *userid = NULL; + char *realm = NULL; + const char *user_realm = NULL; + int ret = SASL_FAIL; + const struct propval *to_store, *cur; + char *user_buf; + + /* just checking if we are enabled */ + if(!ctx) return SASL_OK; + + if(!sparams || !user) return SASL_BADPARAM; + + user_buf = sparams->utils->malloc(ulen + 1); + if(!user_buf) { + ret = SASL_NOMEM; + goto done; + } + + memcpy(user_buf, user, ulen); + user_buf[ulen] = '\0'; + + if(sparams->user_realm) { + user_realm = sparams->user_realm; + } else { + user_realm = sparams->serverFQDN; + } + + ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm, + sparams->serverFQDN, user_buf); + if(ret != SASL_OK) goto done; + + to_store = sparams->utils->prop_get(ctx); + if(!to_store) { + ret = SASL_BADPARAM; + goto done; + } + + ret = SASL_OK; + for (cur = to_store; cur->name; cur++) { + const char *value = (cur->values && cur->values[0]) ? cur->values[0] : NULL; + + if (cur->name[0] == '*') { + continue; + } + + /* WARN: We only support one value right now. */ + ret = _sasldb_putdata(sparams->utils, + sparams->utils->conn, + userid, + realm, + cur->name, + value, + value ? strlen(value) : 0); + + if (value == NULL && ret == SASL_NOUSER) { + /* Deleting something which is not there is not an error */ + ret = SASL_OK; + } + + if (ret != SASL_OK) { + /* We've already failed, no point in continuing */ + break; + } + } + + done: + if (userid) sparams->utils->free(userid); + if (realm) sparams->utils->free(realm); + if (user_buf) sparams->utils->free(user_buf); + + return ret; +} + +static sasl_auxprop_plug_t sasldb_auxprop_plugin = { + 0, /* Features */ + 0, /* spare */ + NULL, /* glob_context */ + sasldb_auxprop_free, /* auxprop_free */ + sasldb_auxprop_lookup, /* auxprop_lookup */ + "sasldb", /* name */ + sasldb_auxprop_store /* auxprop_store */ +}; + +int sasldb_auxprop_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_auxprop_plug_t **plug, + const char *plugname __attribute__((unused))) +{ + if(!out_version || !plug) return SASL_BADPARAM; + + /* Do we have database support? */ + /* Note that we can use a NULL sasl_conn_t because our + * sasl_utils_t is "blessed" with the global callbacks */ + if(_sasl_check_db(utils, NULL) != SASL_OK) + return SASL_NOMECH; + + /* Check if libsasl API is older than ours. If it is, fail */ + if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS; + + *out_version = SASL_AUXPROP_PLUG_VERSION; + + *plug = &sasldb_auxprop_plugin; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/plugins/scram.c b/contrib/libs/sasl/plugins/scram.c new file mode 100644 index 0000000000..36f61086b8 --- /dev/null +++ b/contrib/libs/sasl/plugins/scram.c @@ -0,0 +1,3087 @@ +/* SCRAM-SHA-1/SHA-2 SASL plugin + * Alexey Melnikov + */ +/* + * Copyright (c) 2009-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#ifndef macintosh +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <errno.h> + +#include <sasl.h> +#include <saslplug.h> +#include <saslutil.h> + +#include "plugin_common.h" + +#ifdef macintosh +#error #include <sasl_scram_plugin_decl.h> +#endif + +#include <openssl/sha.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> + +/***************************** Common Section *****************************/ + +#define NONCE_SIZE (32) /* arbitrary */ +#define SALT_SIZE (16) /* arbitrary */ + +/* TODO: make this a configurable option? */ +#define DEFAULT_ITERATION_COUNTER 4096 +#define MIN_ITERATION_COUNTER 4096 + +#define MAX_ITERATION_COUNTER 0x10000 + +/* maximum length of the iteration_counter (as a string). Assume it is 32bits */ +#define ITERATION_COUNTER_BUF_LEN 20 + +#define BASE64_LEN(size) (((size) / 3 * 4) + (((size) % 3) ? 4 : 0)) + +#define MAX_CLIENTIN_LEN 2048 +#define MAX_SERVERIN_LEN 2048 + +#define STRINGIZE(x) #x +#define MAX_CLIENTIN_LEN_STR STRINGIZE((MAX_CLIENTIN_LEN)) +#define MAX_SERVERIN_LEN_STR STRINGIZE((MAX_SERVERIN_LEN)) + +#define CLIENT_KEY_CONSTANT "Client Key" +#define SERVER_KEY_CONSTANT "Server Key" +#define CLIENT_KEY_CONSTANT_LEN sizeof(CLIENT_KEY_CONSTANT)-1 +#define SERVER_KEY_CONSTANT_LEN sizeof(SERVER_KEY_CONSTANT)-1 + +#define SCRAM_CB_FLAG_MASK 0x0F +#define SCRAM_CB_FLAG_N 0x00 +#define SCRAM_CB_FLAG_P 0x01 +#define SCRAM_CB_FLAG_Y 0x02 + +#ifdef SCRAM_DEBUG +#define PRINT_HASH(func,hash,size) print_hash(func,hash,size) +#else +#define PRINT_HASH(func,hash,size) +#endif + +/* NB: A temporary mapping for "internal errors". It would be better to add + a new SASL error code for that */ +#define SASL_SCRAM_INTERNAL SASL_NOMEM + + +/* Holds the core salt to avoid regenerating salt each auth. */ +static unsigned char g_salt_key[SALT_SIZE]; + +/* Note that currently only SHA-* variants are supported! */ +static const char * +scram_sasl_mech_name(size_t hash_size) +{ + switch (hash_size) { + case 64: + return "SCRAM-SHA-512"; + + case 48: + return "SCRAM-SHA-384"; + + case 32: + return "SCRAM-SHA-256"; + + case 28: + return "SCRAM-SHA-224"; + + case 20: + return "SCRAM-SHA-1"; + } + + return NULL; +} + +/* Convert saslname = 1*(value-safe-char / "=2C" / "=3D") in place. + Returns SASL_FAIL if the encoding is invalid, otherwise SASL_OK */ +static int +decode_saslname (char *buf) +{ + char * inp; + char * outp; + + inp = outp = buf; + + while (*inp) { + if (*inp == '=') { + inp++; + if (*inp == '\0') { + return SASL_FAIL; + } + if (inp[0] == '2' && inp[1] == 'C') { + *outp = ','; + inp += 2; + } else if (inp[0] == '3' && inp[1] == 'D') { + *outp = '='; + inp += 2; + } else { + return SASL_FAIL; + } + } else { + *outp = *inp; + inp++; + } + outp++; + } + + *outp = '\0'; + + return SASL_OK; +} + +/* Convert a username to saslname = 1*(value-safe-char / "=2C" / "=3D") + and return an allocated copy. + "freeme" contains pointer to the allocated output, or NULL, + if encoded_saslname just points to saslname. + Returns SASL_NOMEM if can't allocate memory for the output, otherwise SASL_OK */ +static int +encode_saslname (const char *saslname, + const char **encoded_saslname, + char **freeme) +{ + const char * inp; + char * outp; + int special_chars = 0; + + /* Found out if anything needs encoding */ + for (inp = saslname; *inp; inp++) { + if (*inp == ',' || *inp == '=') { + special_chars++; + } + } + + if (special_chars == 0) { + *encoded_saslname = saslname; + *freeme = NULL; + return SASL_OK; + } + + outp = malloc(strlen(saslname) + special_chars * 2 + 1); + *encoded_saslname = outp; + *freeme = outp; + if (outp == NULL) { + return SASL_NOMEM; + } + + for (inp = saslname; *inp; inp++) { + switch (*inp) { + case ',': + *outp++ = '='; + *outp++ = '2'; + *outp++ = 'C'; + break; + + case '=': + *outp++ = '='; + *outp++ = '3'; + *outp++ = 'D'; + break; + + default: + *outp++ = *inp; + } + } + + *outp = '\0'; + + return SASL_OK; +} + +static char * +create_nonce(const sasl_utils_t * utils, + char *buffer, + size_t buflen) /* Including the terminating NUL */ +{ + char *intbuf; + unsigned int estimated; + + if ((buflen - 1) % 4 != 0) { + /* NB: the algorithm below doesn't work for such length. + It needs to be adjusted to allocate + 4 bytes, + encode the last 4 bytes to a separate buffer and + then copy the necessary number of bytes to the end of the output */ + return NULL; + } + + estimated = (unsigned int)((buflen - 1) / 4 * 3); + intbuf = (char *) utils->malloc(estimated + 1); + if (intbuf == NULL) { + return NULL; + } + + utils->rand(utils->rpool, intbuf, estimated); + + /* base 64 encode it so it has valid chars */ + if (utils->encode64(intbuf, + estimated, + buffer, + (unsigned int)buflen, + NULL) != SASL_OK) { + utils->free(intbuf); + return NULL; + } + + utils->free(intbuf); + + buffer[buflen-1] = '\0'; + + return buffer; +} + +#ifdef SCRAM_DEBUG +/* Useful for debugging interop issues */ +static void +print_hash (const char * func, const char * hash, size_t hash_size) +{ + int i; + + printf (" HASH in %s:", func); + for (i = 0; i < hash_size; i++) { + printf (" %.2X", (unsigned char)hash[i]); + } + printf ("\n"); +} +#endif + + +/* The result variable need to point to a buffer big enough for the [SHA-*] hash */ +static void +Hi (const sasl_utils_t * utils, + const EVP_MD *md, + const char * str, + size_t str_len, + const char * salt, + size_t salt_len, + unsigned int iteration_count, + char * result) +{ + char * initial_key = NULL; + unsigned int i; + char * temp_result; + unsigned int hash_len = 0; + size_t k, hash_size = EVP_MD_size(md); + + initial_key = utils->malloc(salt_len + 4); + memcpy (initial_key, salt, salt_len); + initial_key[salt_len] = 0; + initial_key[salt_len+1] = 0; + initial_key[salt_len+2] = 0; + initial_key[salt_len+3] = 1; + + temp_result = utils->malloc(hash_size); + + /* U1 := HMAC(str, salt || INT(1)) */ + + if (HMAC(md, + (const unsigned char *) str, + (int)str_len, + (const unsigned char *) initial_key, + (int)salt_len + 4, + (unsigned char *)result, + &hash_len) == NULL) { + } + + memcpy(temp_result, result, hash_size); + + PRINT_HASH ("first HMAC in Hi()", temp_result, hash_size); + + /* On each loop iteration j "temp_result" contains Uj, + while "result" contains "U1 XOR ... XOR Uj" */ + for (i = 2; i <= iteration_count; i++) { + if (HMAC(md, + (const unsigned char *) str, + (int)str_len, + (const unsigned char *) temp_result, + hash_size, + (unsigned char *)temp_result, + &hash_len) == NULL) { + } + + PRINT_HASH ("Hi() HMAC inside loop", temp_result, hash_size); + + for (k = 0; k < hash_size; k++) { + result[k] ^= temp_result[k]; + } + + PRINT_HASH ("Hi() - accumulated result inside loop", result, hash_size); + } + + utils->free(initial_key); + utils->free(temp_result); +} + +/** + * User salt is Hi(username,salt_key); + * This is fixed per reboot, to allow caching of SCRAM + * SaltedPassword. + */ +static unsigned char * +scram_server_user_salt(const sasl_utils_t * utils, + const EVP_MD *md, + const char * username, + size_t * p_salt_len) +{ + size_t hash_size = EVP_MD_size(md); + char * result = utils->malloc(hash_size); + Hi(utils, md, username, strlen(username), (const char *) g_salt_key, SALT_SIZE, + 20 /* iterations */, result); + *p_salt_len = hash_size; + return (unsigned char *) result; +} + +static int +GenerateScramSecrets (const sasl_utils_t * utils, + const EVP_MD *md, + const char * password, + size_t password_len, + char * salt, + size_t salt_len, + unsigned int iteration_count, + char * StoredKey, + char * ServerKey, + char ** error_text) +{ + char SaltedPassword[EVP_MAX_MD_SIZE]; + char ClientKey[EVP_MAX_MD_SIZE]; + sasl_secret_t *sec = NULL; + unsigned int hash_len = 0; + int result; + size_t hash_size = EVP_MD_size(md); + + *error_text = NULL; + + if (password_len == 0) { + *error_text = "empty secret"; + result = SASL_FAIL; + goto cleanup; + } + + sec = utils->malloc(sizeof(sasl_secret_t) + password_len); + if (sec == NULL) { + result = SASL_NOMEM; + goto cleanup; + } + + sec->len = (unsigned) password_len; + strncpy((char *)sec->data, password, password_len + 1); + + /* SaltedPassword := Hi(password, salt) */ + Hi (utils, + md, + (const char *) sec->data, + sec->len, + salt, + salt_len, + iteration_count, + SaltedPassword); + + /* ClientKey := HMAC(SaltedPassword, "Client Key") */ + if (HMAC(md, + (const unsigned char *) SaltedPassword, + hash_size, + (const unsigned char *) CLIENT_KEY_CONSTANT, + CLIENT_KEY_CONSTANT_LEN, + (unsigned char *)ClientKey, + &hash_len) == NULL) { + *error_text = "HMAC call failed"; + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + /* StoredKey := H(ClientKey) */ + if (EVP_Digest((const unsigned char *) ClientKey, hash_size, + (unsigned char *) StoredKey, NULL, md, NULL) == 0) { + *error_text = "Digest call failed"; + result = SASL_SCRAM_INTERNAL; + goto cleanup; + + } + + /* ServerKey := HMAC(SaltedPassword, "Server Key") */ + if (HMAC(md, + (const unsigned char *) SaltedPassword, + hash_size, + (const unsigned char *) SERVER_KEY_CONSTANT, + SERVER_KEY_CONSTANT_LEN, + (unsigned char *)ServerKey, + &hash_len) == NULL) { + *error_text = "HMAC call failed"; + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + result = SASL_OK; + +cleanup: + if (sec) { + _plug_free_secret(utils, &sec); + } + return result; +} + +/***************************** Server Section *****************************/ + +typedef struct server_context { + int state; + + const EVP_MD *md; /* underlying MDA */ + + char * authentication_id; + char * authorization_id; + + char * out_buf; + unsigned out_buf_len; + char * auth_message; + size_t auth_message_len; + char * nonce; + /* in binary form */ + char * salt; + size_t salt_len; + unsigned int iteration_count; + char StoredKey[EVP_MAX_MD_SIZE + 1]; + char ServerKey[EVP_MAX_MD_SIZE + 1]; + + int cb_flags; + char *cbindingname; + char *gs2_header; + size_t gs2_header_length; +} server_context_t; + +static int +scram_server_mech_new(void *glob_context, + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + server_context_t *text; + + /* holds state are in */ + text = sparams->utils->malloc(sizeof(server_context_t)); + if (text == NULL) { + MEMERROR( sparams->utils ); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(server_context_t)); + /* text->state = 0; */ + + text->md = EVP_get_digestbyname((const char *) glob_context); + + *conn_context = text; + + return SASL_OK; +} + +static int +scram_server_mech_step1(server_context_t *text, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams __attribute__((unused))) +{ + char * authorization_id; + char * authentication_id; + char * p; + char * nonce; + size_t client_nonce_len; + char * base64_salt = NULL; + size_t base64len; + size_t estimated_challenge_len; + size_t pure_scram_length; + char * inbuf = NULL; + const char *password_request[] = { SASL_AUX_PASSWORD, + "*authPassword", + NULL }; + int canon_flags; + struct propval auxprop_values[3]; + int result; + size_t hash_size = EVP_MD_size(text->md); + const char *scram_sasl_mech = scram_sasl_mech_name(hash_size); + + if (clientinlen == 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "%s input expected", scram_sasl_mech); + return SASL_BADPROT; + } + + /* Expecting: 'gs2-cbind-flag "," [ authzid ] "," [reserved-mext ","] + username "," nonce ["," extensions]' */ + + if (clientinlen < 10) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid %s input", scram_sasl_mech); + return SASL_BADPROT; + } + + inbuf = sparams->utils->malloc (clientinlen + 1); + + if (inbuf == NULL) { + MEMERROR( sparams->utils ); + return SASL_NOMEM; + } + + memcpy(inbuf, clientin, clientinlen); + inbuf[clientinlen] = 0; + + if (strlen(inbuf) != clientinlen) { + sparams->utils->seterror(sparams->utils->conn, 0, + "NULs found in %s input", scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + p = inbuf; + + /* gs2-cbind-flag = "p=" cb-name / "n" / "y" + ;; "n" -> client doesn't support channel binding + ;; "y" -> client does support channel binding + ;; but thinks the server does not. + ;; "p" -> client requires channel binding. + ;; The selected channel binding follows "p=". */ + switch (p[0]) { + case 'p': + if (p[1] != '=') { + sparams->utils->seterror(sparams->utils->conn, 0, + "The initial 'p' needs to be followed by '=' in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + p++; + + text->cbindingname = p + 1; + p = strchr (p, ','); + if (p == NULL) { + text->cbindingname = NULL; + + sparams->utils->seterror(sparams->utils->conn, 0, + "Channel binding name must be terminated by a comma in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + *p = '\0'; + _plug_strdup(sparams->utils, text->cbindingname, &text->cbindingname, NULL); + *p = ','; + + text->cb_flags = SCRAM_CB_FLAG_P; + break; + + case 'n': + text->cb_flags = SCRAM_CB_FLAG_N; + /* We always have at least 10 bytes, so this is safe */ + p++; + break; + + case 'y': + text->cb_flags = SCRAM_CB_FLAG_Y; + /* We always have at least 10 bytes, so this is safe */ + p++; + break; + + default: + sparams->utils->seterror(sparams->utils->conn, 0, + "The initial %s client response needs to start with 'y', 'n' or 'p'", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (p[0] != ',') { + sparams->utils->seterror(sparams->utils->conn, 0, + "',' expected in %s input", scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + p++; + + if (p[0] == 'a' && p[1] == '=') { + authorization_id = p + 2; + + p = strchr (authorization_id, ','); + if (p == NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, + "At least nonce is expected in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + /* End of the GS2 header */ + p[0] = '\0'; + /* The GS2 header length DOES include the terminating comma */ + text->gs2_header_length = p - inbuf + 1; + + p++; + + /* Make a read-write copy we can modify */ + _plug_strdup(sparams->utils, authorization_id, &text->authorization_id, NULL); + + if (decode_saslname(text->authorization_id) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid authorization identity encoding in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + } else if (p[0] != ',') { + sparams->utils->seterror(sparams->utils->conn, 0, + "',' expected in %s input", scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } else { + /* End of the GS2 header */ + p[0] = '\0'; + /* The GS2 header length DOES include the terminating comma */ + text->gs2_header_length = p - inbuf + 1; + + p++; + } + + text->gs2_header = sparams->utils->malloc (text->gs2_header_length + 1); + if (text->gs2_header == NULL) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + memcpy(text->gs2_header, inbuf, text->gs2_header_length - 1); + /* Remember the comma */ + text->gs2_header[text->gs2_header_length - 1] = ','; + text->gs2_header[text->gs2_header_length] = 0; + + + + if (p[1] != '=') { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid %s input", scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (p[0] == 'm') { + sparams->utils->seterror(sparams->utils->conn, 0, + "Unsupported mandatory extension to %s", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (p[0] != 'n') { + sparams->utils->seterror(sparams->utils->conn, 0, + "Username (n=) expected in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + authentication_id = p + 2; + p = strchr (authentication_id, ','); + + /* MUST be followed by a nonce */ + if (p == NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Nonce expected after the username in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + *p = '\0'; + p++; + + if (decode_saslname(authentication_id) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid username encoding in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + _plug_strdup(sparams->utils, authentication_id, &text->authentication_id, NULL); + + if (strncmp(p, "r=", 2) != 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Nonce expected after the username in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + p += 2; + nonce = p; + p = strchr (nonce, ','); + + if (p == NULL) { + p = nonce + strlen(nonce); + } else { + *p = '\0'; + } + + /* Generate server nonce, by appending some random stuff to the client nonce */ + client_nonce_len = strlen(nonce); + text->nonce = sparams->utils->malloc (client_nonce_len + NONCE_SIZE + 1); + + if (text->nonce == NULL) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + strcpy (text->nonce, nonce); + + if (create_nonce(sparams->utils, + text->nonce + client_nonce_len, + NONCE_SIZE + 1) == NULL) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + + + /* Now we fetch user's password and calculate our secret */ + result = sparams->utils->prop_request(sparams->propctx, password_request); + if (result != SASL_OK) { + goto cleanup; + } + + /* this will trigger the getting of the aux properties */ + canon_flags = SASL_CU_AUTHID; + if (text->authorization_id == NULL || *text->authorization_id == '\0') { + canon_flags |= SASL_CU_AUTHZID; + } + + result = sparams->canon_user(sparams->utils->conn, + text->authentication_id, + 0, + canon_flags, + oparams); + if (result != SASL_OK) { + SETERROR(sparams->utils, "unable to canonify user and get auxprops"); + goto cleanup; + } + + if (text->authorization_id != NULL && *text->authorization_id != '\0') { + result = sparams->canon_user(sparams->utils->conn, + text->authorization_id, + 0, + SASL_CU_AUTHZID, + oparams); + } + if (result != SASL_OK) { + SETERROR(sparams->utils, "unable to canonify authorization ID"); + goto cleanup; + } + + result = sparams->utils->prop_getnames(sparams->propctx, + password_request, + auxprop_values); + if (result < 0 || + ((!auxprop_values[0].name || !auxprop_values[0].values) && + (!auxprop_values[1].name || !auxprop_values[1].values))) { + /* We didn't find this username */ + sparams->utils->seterror(sparams->utils->conn,0, + "no secret in database"); + result = sparams->transition ? SASL_TRANS : SASL_NOUSER; + goto cleanup; + } + + if (auxprop_values[0].name && auxprop_values[0].values) { + char * error_text = NULL; + char * s_iteration_count; + char * end; + + text->salt = (char *) scram_server_user_salt(sparams->utils, text->md, text->authentication_id, &text->salt_len); + + sparams->utils->getopt(sparams->utils->getopt_context, + /* Different SCRAM hashes can have different strengh */ + scram_sasl_mech, + "scram_iteration_counter", + (const char **) &s_iteration_count, + NULL); + + if (s_iteration_count != NULL) { + errno = 0; + text->iteration_count = strtoul(s_iteration_count, &end, 10); + if (s_iteration_count == end || *end != '\0' || errno != 0) { + sparams->utils->log(NULL, + SASL_LOG_DEBUG, + "Invalid iteration-count in scram_iteration_count SASL option: not a number. Using the default instead."); + s_iteration_count = NULL; + } + } + + if (s_iteration_count == NULL) { + text->iteration_count = DEFAULT_ITERATION_COUNTER; + } + + result = GenerateScramSecrets (sparams->utils, + text->md, + auxprop_values[0].values[0], + strlen(auxprop_values[0].values[0]), + text->salt, + text->salt_len, + text->iteration_count, + text->StoredKey, + text->ServerKey, + &error_text); + if (result != SASL_OK) { + if (error_text != NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, "%s", + error_text); + } + goto cleanup; + } + + } else if (auxprop_values[1].name && auxprop_values[1].values) { + char s_iteration_count[ITERATION_COUNTER_BUF_LEN+1]; + size_t base64_salt_len; + unsigned int exact_key_len; + const char * scram_hash; + const char * p_field; + char * end; + int i; + size_t scram_sasl_mech_len = strlen(scram_sasl_mech); + + result = SASL_SCRAM_INTERNAL; + + for (i = 0; auxprop_values[1].values[i] != NULL; i++) { + scram_hash = auxprop_values[1].values[i]; + + /* Skip the leading spaces */ + while (*scram_hash == ' ') { + scram_hash++; + } + + if (strncmp(scram_hash, scram_sasl_mech, scram_sasl_mech_len) != 0) { + continue; + } + scram_hash += scram_sasl_mech_len; + + /* Skip spaces */ + while (*scram_hash == ' ') { + scram_hash++; + } + + if (*scram_hash != '$') { + /* syntax error, ignore the value */ + continue; + } + scram_hash++; + + /* Skip spaces */ + while (*scram_hash == ' ') { + scram_hash++; + } + + p_field = strchr(scram_hash, ':'); + if (p_field == NULL || p_field == scram_hash) { + /* syntax error, ignore the value */ + continue; + } + + if ((p_field - scram_hash) > ITERATION_COUNTER_BUF_LEN) { + /* The iteration counter is too big for us */ + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid iteration-count in %s input: the value is too big", + scram_sasl_mech); + continue; + } + + memcpy(s_iteration_count, scram_hash, p_field - scram_hash); + s_iteration_count[p_field - scram_hash] = '\0'; + + errno = 0; + text->iteration_count = strtoul(s_iteration_count, &end, 10); + if (s_iteration_count == end || *end != '\0' || errno != 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid iteration-count in %s input: not a number", + scram_sasl_mech); + continue; + } + + scram_hash = p_field + 1; + + p_field = scram_hash + strcspn(scram_hash, "$ "); + if (p_field == scram_hash || *p_field == '\0') { + /* syntax error, ignore the value */ + continue; + } + + base64_salt_len = p_field - scram_hash; + text->salt = (char *) sparams->utils->malloc(base64_salt_len); + if (sparams->utils->decode64(scram_hash, + (unsigned int)base64_salt_len, + text->salt, + (unsigned int)base64_salt_len, + (unsigned int *) &text->salt_len) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid base64 encoding of the salt in %s stored value", + scram_sasl_mech); + continue; + } + + scram_hash = p_field; + + /* Skip spaces */ + while (*scram_hash == ' ') { + scram_hash++; + } + + if (*scram_hash != '$') { + /* syntax error, ignore the value */ + sparams->utils->free(text->salt); + text->salt = NULL; + continue; + } + scram_hash++; + + /* Skip spaces */ + while (*scram_hash == ' ') { + scram_hash++; + } + + p_field = strchr(scram_hash, ':'); + if (p_field == NULL || p_field == scram_hash) { + /* syntax error, ignore the value */ + sparams->utils->free(text->salt); + text->salt = NULL; + continue; + } + + if (sparams->utils->decode64(scram_hash, + (unsigned int)(p_field - scram_hash), + text->StoredKey, + hash_size + 1, + &exact_key_len) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid base64 encoding of StoredKey in %s per-user storage", + scram_sasl_mech); + sparams->utils->free(text->salt); + text->salt = NULL; + continue; + } + + if (exact_key_len != hash_size) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid StoredKey in %s per-user storage", + scram_sasl_mech); + sparams->utils->free(text->salt); + text->salt = NULL; + continue; + } + + scram_hash = p_field + 1; + + p_field = strchr(scram_hash, ' '); + if (p_field == NULL) { + p_field = scram_hash + strlen(scram_hash); + } + + + if (sparams->utils->decode64(scram_hash, + (unsigned int)(p_field - scram_hash), + text->ServerKey, + hash_size + 1, + &exact_key_len) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid base64 encoding of ServerKey in %s per-user storage", + scram_sasl_mech); + sparams->utils->free(text->salt); + text->salt = NULL; + continue; + } + + if (exact_key_len != hash_size) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid ServerKey in %s per-user storage", scram_sasl_mech); + sparams->utils->free(text->salt); + text->salt = NULL; + continue; + } + + result = SASL_OK; + break; + } + + if (result != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, + 0, "No valid %s secret found", + scram_sasl_mech); + goto cleanup; + } + + } else { + sparams->utils->seterror(sparams->utils->conn, + 0, + "Have neither type of secret"); + return SASL_FAIL; + } + + /* erase the plaintext password */ + sparams->utils->prop_erase(sparams->propctx, password_request[0]); + + + + /* base 64 encode it so it has valid chars */ + base64len = (text->salt_len / 3 * 4) + ((text->salt_len % 3) ? 4 : 0); + + base64_salt = (char *) sparams->utils->malloc(base64len + 1); + if (base64_salt == NULL) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + /* + * Returns SASL_OK on success, SASL_BUFOVER if result won't fit + */ + if (sparams->utils->encode64(text->salt, + (unsigned int)text->salt_len, + base64_salt, + (unsigned int)base64len + 1, + NULL) != SASL_OK) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + base64_salt[base64len] = '\0'; + + /* Now we generate server challenge */ + estimated_challenge_len = client_nonce_len + NONCE_SIZE + + base64len + + ITERATION_COUNTER_BUF_LEN + + strlen("r=,s=,i="); + result = _plug_buf_alloc(sparams->utils, + &(text->out_buf), + &(text->out_buf_len), + (unsigned) estimated_challenge_len + 1); + if (result != SASL_OK) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + sprintf(text->out_buf, + "r=%s,s=%s,i=%u", + text->nonce, + base64_salt, + text->iteration_count); + + + /* Save the (client response, ",", server challenge, ","). + Note, we skip the GS2 prefix here */ + pure_scram_length = clientinlen - text->gs2_header_length; + text->auth_message_len = pure_scram_length + 1 + estimated_challenge_len + 1; + text->auth_message = sparams->utils->malloc (text->auth_message_len + 1); + if (text->auth_message == NULL) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + memcpy(text->auth_message, clientin + text->gs2_header_length, pure_scram_length); + text->auth_message[pure_scram_length] = ','; + strcpy (text->auth_message + pure_scram_length + 1, text->out_buf); + strcat (text->auth_message + pure_scram_length + 1, ","); + + /* Now remember the exact length, not the estimated one */ + text->auth_message_len = strlen(text->auth_message); + + *serverout = text->out_buf; + *serveroutlen = (unsigned) strlen(text->out_buf); + + result = SASL_CONTINUE; + text->state = 2; + +cleanup: + if (inbuf != NULL) { + sparams->utils->free(inbuf); + } + if (base64_salt != NULL) { + sparams->utils->free(base64_salt); + } + return result; +} + +static int +scram_server_mech_step2(server_context_t *text, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + char *channel_binding = NULL; + size_t channel_binding_len = 0; + char *binary_channel_binding = NULL; + unsigned binary_channel_binding_len = 0; + char *client_proof = NULL; + char *inbuf = NULL; + char *p; + int result = SASL_FAIL; + size_t proof_offset; + char * full_auth_message; + char ReceivedClientKey[EVP_MAX_MD_SIZE]; + char DecodedClientProof[EVP_MAX_MD_SIZE + 1]; + char CalculatedStoredKey[EVP_MAX_MD_SIZE]; + char ClientSignature[EVP_MAX_MD_SIZE]; + char ServerSignature[EVP_MAX_MD_SIZE]; + char * nonce; + size_t client_proof_len; + size_t server_proof_len; + unsigned exact_client_proof_len; + unsigned int hash_len = 0; + size_t k, hash_size = EVP_MD_size(text->md); + const char *scram_sasl_mech = scram_sasl_mech_name(hash_size); + + if (clientinlen == 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "%s input expected", scram_sasl_mech); + return SASL_BADPROT; + } + + if (clientinlen < 3 || clientin[1] != '=') { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid %s input", scram_sasl_mech); + return SASL_BADPROT; + } + + inbuf = sparams->utils->malloc (clientinlen + 1); + + if (inbuf == NULL) { + MEMERROR( sparams->utils ); + return SASL_NOMEM; + } + + memcpy(inbuf, clientin, clientinlen); + inbuf[clientinlen] = 0; + + if (strlen(inbuf) != clientinlen) { + sparams->utils->seterror(sparams->utils->conn, 0, + "NULs found in %s input", scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + /* Expecting: channel-binding "," nonce ["," extensions] "," proof */ + + p = inbuf; + + if (strncmp(p, "c=", 2) != 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Channel binding expected in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + channel_binding = p + 2; + + p = strchr (channel_binding, ','); + if (p == NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, + "At least nonce is expected in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + *p = '\0'; + p++; + + channel_binding_len = strlen(channel_binding); + + /* We can calculate the exact length, but the decoded (binary) data + is always shorter than its base64 version. */ + binary_channel_binding = (char *) sparams->utils->malloc(channel_binding_len + 1); + + if (sparams->utils->decode64(channel_binding, + (unsigned int)channel_binding_len, + binary_channel_binding, + (unsigned int)channel_binding_len, + &binary_channel_binding_len) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid base64 encoding of the channel bindings in %s", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (binary_channel_binding_len < text->gs2_header_length || + strncmp(binary_channel_binding, text->gs2_header, text->gs2_header_length) != 0) { + sparams->utils->seterror (sparams->utils->conn, + 0, + "Channel bindings prefix doesn't match the one received in the GS2 header of %s. Expected \"%s\"", + scram_sasl_mech, text->gs2_header); + result = SASL_BADPROT; + goto cleanup; + } + + switch (text->cb_flags & SCRAM_CB_FLAG_MASK) { + case SCRAM_CB_FLAG_P: + binary_channel_binding_len -= (unsigned)text->gs2_header_length; + if (binary_channel_binding_len == 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Channel bindings data expected in %s", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (sparams->cbinding == NULL) { + sparams->utils->seterror (sparams->utils->conn, + 0, + "Server does not support channel binding type received in %s. Received: %s", + scram_sasl_mech, + text->cbindingname); + result = SASL_BADPROT; + goto cleanup; + } + + if (strcmp(sparams->cbinding->name, text->cbindingname) != 0) { + sparams->utils->seterror (sparams->utils->conn, + 0, + "Unsupported channel bindings type received in %s. Expected: %s, received: %s", + scram_sasl_mech, + sparams->cbinding->name, + text->cbindingname); + result = SASL_BADPROT; + goto cleanup; + } + + if (binary_channel_binding_len != sparams->cbinding->len) { + sparams->utils->seterror (sparams->utils->conn, + 0, + "Unsupported channel bindings length received in %s. Expected length: %lu, received: %d", + scram_sasl_mech, + sparams->cbinding->len, + binary_channel_binding_len); + result = SASL_BADPROT; + goto cleanup; + } + + if (memcmp(binary_channel_binding + text->gs2_header_length, + sparams->cbinding->data, + binary_channel_binding_len) != 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Channel bindings mismatch in %s", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + break; + } + + if (strncmp(p, "r=", 2) != 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Nonce expected in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + nonce = p + 2; + + p = strchr (nonce, ','); + if (p == NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, + "At least proof is expected in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + *p = '\0'; + p++; + + if (strcmp(nonce, text->nonce) != 0) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Nonce mismatch %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + while (p[0] != '\0') { + if (strncmp(p, "p=", 2) == 0) { + client_proof = p + 2; + proof_offset = p - inbuf - 1; + break; + } + + p = strchr (p, ','); + if (p == NULL) { + break; + } + p++; + } + + if (client_proof == NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Client proof is expected in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + /* Check that no extension data exists after the proof */ + p = strchr (client_proof, ','); + if (p != NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, + "No extension data is allowed after the client proof in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (strlen(client_proof) != (hash_size / 3 * 4 + (hash_size % 3 ? 4 : 0))) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid client proof length in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + /* Construct the full AuthMessage */ + full_auth_message = sparams->utils->realloc(text->auth_message, + text->auth_message_len + proof_offset + 1); + if (full_auth_message == NULL) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + text->auth_message = full_auth_message; + + memcpy(text->auth_message + text->auth_message_len, clientin, proof_offset); + + text->auth_message_len += proof_offset; + text->auth_message[text->auth_message_len] = '\0'; + + + /* ClientSignature := HMAC(StoredKey, AuthMessage) */ + if (HMAC(text->md, + (const unsigned char *) text->StoredKey, + hash_size, + (const unsigned char *)text->auth_message, + (int)text->auth_message_len, + (unsigned char *)ClientSignature, + &hash_len) == NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, + "HMAC-%s call failed", scram_sasl_mech+6); + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + client_proof_len = strlen(client_proof); + if (sparams->utils->decode64(client_proof, + (unsigned int)client_proof_len, + DecodedClientProof, + hash_size + 1, + &exact_client_proof_len) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid base64 encoding of the client proof in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (exact_client_proof_len != hash_size) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Invalid client proof (truncated) in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + for (k = 0; k < hash_size; k++) { + ReceivedClientKey[k] = DecodedClientProof[k] ^ ClientSignature[k]; + } + + /* StoredKey := H(ClientKey) */ + if (EVP_Digest((const unsigned char *) ReceivedClientKey, hash_size, + (unsigned char *) CalculatedStoredKey, NULL, text->md, NULL) == 0) { + sparams->utils->seterror(sparams->utils->conn,0, + "%s call failed", scram_sasl_mech+6); + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + for (k = 0; k < hash_size; k++) { + if (CalculatedStoredKey[k] != text->StoredKey[k]) { + SETERROR(sparams->utils, "StoredKey mismatch"); + result = SASL_BADAUTH; + goto cleanup; + } + } + + /* ServerSignature := HMAC(ServerKey, AuthMessage) */ + if (HMAC(text->md, + (const unsigned char *) text->ServerKey, + hash_size, + (unsigned char *) text->auth_message, + (int)text->auth_message_len, + (unsigned char *)ServerSignature, + &hash_len) == NULL) { + sparams->utils->seterror(sparams->utils->conn,0, + "HMAC-%s call failed", scram_sasl_mech+6); + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + server_proof_len = (hash_size / 3 * 4 + (hash_size % 3 ? 4 : 0)); + result = _plug_buf_alloc(sparams->utils, + &(text->out_buf), + &(text->out_buf_len), + (unsigned) server_proof_len + strlen("v=") + 1); + if (result != SASL_OK) { + MEMERROR( sparams->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + text->out_buf[0] = 'v'; + text->out_buf[1] = '='; + + + if (sparams->utils->encode64(ServerSignature, + hash_size, + text->out_buf+2, + (unsigned int)server_proof_len + 1, + NULL) != SASL_OK) { + SETERROR(sparams->utils, "Internal error"); + /* This is not quite right, but better than alternatives */ + result = SASL_NOMEM; + goto cleanup; + } + + text->out_buf[server_proof_len + 2] = '\0'; + + *serverout = text->out_buf; + *serveroutlen = (unsigned) strlen(text->out_buf); + + + /* set oparams */ + + switch (text->cb_flags & SCRAM_CB_FLAG_MASK) { + case SCRAM_CB_FLAG_N: + oparams->cbindingdisp = SASL_CB_DISP_NONE; + break; + case SCRAM_CB_FLAG_P: + oparams->cbindingdisp = SASL_CB_DISP_USED; + oparams->cbindingname = text->cbindingname; + break; + case SCRAM_CB_FLAG_Y: + oparams->cbindingdisp = SASL_CB_DISP_WANT; + break; + } + + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + result = SASL_OK; + +cleanup: + if (inbuf != NULL) { + sparams->utils->free(inbuf); + } + if (binary_channel_binding != NULL) { + sparams->utils->free(binary_channel_binding); + } + + return result; +} + +static int scram_server_mech_step(void *conn_context, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + server_context_t *text = (server_context_t *) conn_context; + const char *scram_sasl_mech = NULL; + + *serverout = NULL; + *serveroutlen = 0; + + if (text == NULL) { + return SASL_BADPROT; + } + + scram_sasl_mech = scram_sasl_mech_name(EVP_MD_size(text->md)); + + /* this should be well more than is ever needed */ + if (clientinlen > MAX_CLIENTIN_LEN) { + sparams->utils->seterror(sparams->utils->conn, 0, + "%s input longer than " + STRINGIZE((MAX_CLIENTIN_LEN)) " bytes", + scram_sasl_mech); + return SASL_BADPROT; + } + + switch (text->state) { + case 0: + text->state++; + /* Assume the protocol doesn't support initial client response */ + if (clientinlen == 0) { + return SASL_CONTINUE; + } + /* fall through */ + + case 1: + return scram_server_mech_step1(text, + sparams, + clientin, + clientinlen, + serverout, + serveroutlen, + oparams); + + case 2: + text->state++; + return scram_server_mech_step2(text, + sparams, + clientin, + clientinlen, + serverout, + serveroutlen, + oparams); + + default: /* should never get here */ + sparams->utils->log(NULL, SASL_LOG_ERR, + "Invalid %s server step %d\n", + scram_sasl_mech, text->state); + return SASL_FAIL; + } + + return SASL_FAIL; /* should never get here */ +} + +static int scram_setpass(void *glob_context, + sasl_server_params_t *sparams, + const char *userstr, + const char *pass, + unsigned passlen, + const char *oldpass __attribute__((unused)), + unsigned oldpasslen __attribute__((unused)), + unsigned flags) +{ + int r; + char *user = NULL; + char *user_only = NULL; + char *realm = NULL; + sasl_secret_t *sec = NULL; + struct propctx *propctx = NULL; + const char *store_request[] = { "authPassword", + NULL }; + const char *generate_scram_secret; + const EVP_MD *md = EVP_get_digestbyname((const char *) glob_context); + size_t hash_size = EVP_MD_size(md); + const char *scram_sasl_mech = scram_sasl_mech_name(hash_size); + + /* Do we have a backend that can store properties? */ + if (!sparams->utils->auxprop_store || + sparams->utils->auxprop_store(NULL, NULL, NULL) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "%s: auxprop backend can't store properties", + scram_sasl_mech); + return SASL_NOMECH; + } + + sparams->utils->getopt(sparams->utils->getopt_context, + /* This affects all SCRAM plugins, not just SCRAM-SHA-1 */ + "SCRAM", + "scram_secret_generate", + &generate_scram_secret, + NULL); + + /* NOTE: The default (when this option is not set) is NOT to generate authPassword secret */ + if (!(generate_scram_secret && + (generate_scram_secret[0] == '1' || generate_scram_secret[0] == 'y' || + (generate_scram_secret[0] == 'o' && generate_scram_secret[1] == 'n') || + generate_scram_secret[0] == 't'))) { + /* Pretend that everything is Ok, no need to generate noise in the logs */ + return SASL_OK; + } + + r = _plug_parseuser(sparams->utils, + &user_only, + &realm, + sparams->user_realm, + sparams->serverFQDN, + userstr); + if (r) { + sparams->utils->seterror(sparams->utils->conn, 0, + "%s: Error parsing user", scram_sasl_mech); + return r; + } + + r = _plug_make_fulluser(sparams->utils, &user, user_only, realm); + if (r) { + goto cleanup; + } + + if ((flags & SASL_SET_DISABLE) || pass == NULL) { + sec = NULL; + } else { + char * error_text = NULL; + char salt[SALT_SIZE + 1]; + char base64_salt[BASE64_LEN(SALT_SIZE) + 1]; + /* size_t salt_len = SALT_SIZE; */ + char StoredKey[EVP_MAX_MD_SIZE + 1]; + char ServerKey[EVP_MAX_MD_SIZE + 1]; + char base64_StoredKey[BASE64_LEN(EVP_MAX_MD_SIZE) + 1]; + char base64_ServerKey[BASE64_LEN(EVP_MAX_MD_SIZE) + 1]; + size_t secret_len; + unsigned int iteration_count = DEFAULT_ITERATION_COUNTER; + char * s_iteration_count; + char * end; + + sparams->utils->getopt(sparams->utils->getopt_context, + /* Different SCRAM hashes can have different strengh */ + scram_sasl_mech, + "scram_iteration_counter", + (const char **) &s_iteration_count, + NULL); + + if (s_iteration_count != NULL) { + errno = 0; + iteration_count = strtoul(s_iteration_count, &end, 10); + if (s_iteration_count == end || *end != '\0' || errno != 0) { + sparams->utils->log(NULL, + SASL_LOG_DEBUG, + "Invalid iteration-count in scram_iteration_count SASL option: not a number. Using the default instead."); + s_iteration_count = NULL; + } + } + + if (s_iteration_count == NULL) { + iteration_count = DEFAULT_ITERATION_COUNTER; + } + + sparams->utils->rand(sparams->utils->rpool, salt, SALT_SIZE); + + r = GenerateScramSecrets (sparams->utils, + md, + pass, + passlen, + salt, + SALT_SIZE, + iteration_count, + StoredKey, + ServerKey, + &error_text); + if (r != SASL_OK) { + if (error_text != NULL) { + sparams->utils->seterror(sparams->utils->conn, 0, "%s", + error_text); + } + goto cleanup; + } + + /* Returns SASL_OK on success, SASL_BUFOVER if result won't fit */ + if (sparams->utils->encode64(salt, + SALT_SIZE, + base64_salt, + BASE64_LEN(SALT_SIZE) + 1, + NULL) != SASL_OK) { + MEMERROR( sparams->utils ); + r = SASL_NOMEM; + goto cleanup; + } + + base64_salt[BASE64_LEN(SALT_SIZE)] = '\0'; + + + /* Returns SASL_OK on success, SASL_BUFOVER if result won't fit */ + if (sparams->utils->encode64(StoredKey, + hash_size, + base64_StoredKey, + BASE64_LEN(hash_size) + 1, + NULL) != SASL_OK) { + MEMERROR( sparams->utils ); + r = SASL_NOMEM; + goto cleanup; + } + + base64_StoredKey[BASE64_LEN(hash_size)] = '\0'; + + + + /* Returns SASL_OK on success, SASL_BUFOVER if result won't fit */ + if (sparams->utils->encode64(ServerKey, + hash_size, + base64_ServerKey, + BASE64_LEN(hash_size) + 1, + NULL) != SASL_OK) { + MEMERROR( sparams->utils ); + r = SASL_NOMEM; + goto cleanup; + } + + base64_ServerKey[BASE64_LEN(hash_size)] = '\0'; + + secret_len = strlen(scram_sasl_mech) + strlen("$:$:") + + ITERATION_COUNTER_BUF_LEN + + sizeof(base64_salt) + + sizeof(base64_StoredKey) + + sizeof(base64_ServerKey); + + sec = sparams->utils->malloc(sizeof(sasl_secret_t) + secret_len); + if (sec == NULL) { + MEMERROR( sparams->utils ); + r = SASL_NOMEM; + goto cleanup; + } + + sprintf((char *) sec->data, + "%s$%u:%s$%s:%s", + scram_sasl_mech, + iteration_count, + base64_salt, + base64_StoredKey, + base64_ServerKey); + sec->len = (unsigned int) strlen((const char *) sec->data); + } + + /* do the store */ + propctx = sparams->utils->prop_new(0); + if (!propctx) { + r = SASL_FAIL; + } + if (!r) { + r = sparams->utils->prop_request(propctx, store_request); + } + if (!r) { + r = sparams->utils->prop_set(propctx, + "authPassword", + (const char *) (sec ? sec->data : NULL), + (sec ? sec->len : 0)); + } + if (!r) { + r = sparams->utils->auxprop_store(sparams->utils->conn, propctx, user); + } + if (propctx) { + sparams->utils->prop_dispose(&propctx); + } + + if (r) { + sparams->utils->seterror(sparams->utils->conn, 0, + "Error putting %s secret", + scram_sasl_mech); + goto cleanup; + } + + sparams->utils->log(NULL, SASL_LOG_DEBUG, "Setpass for %s successful\n", + scram_sasl_mech); + + cleanup: + if (user) _plug_free_string(sparams->utils, &user); + if (user_only) _plug_free_string(sparams->utils, &user_only); + if (realm) _plug_free_string(sparams->utils, &realm); + if (sec) _plug_free_secret(sparams->utils, &sec); + + return r; +} + +static void scram_server_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + server_context_t *text = (server_context_t *) conn_context; + + if (!text) return; + + if (text->authentication_id) _plug_free_string(utils,&(text->authentication_id)); + if (text->authorization_id) _plug_free_string(utils,&(text->authorization_id)); + if (text->out_buf) _plug_free_string(utils,&(text->out_buf)); + if (text->auth_message) _plug_free_string(utils,&(text->auth_message)); + if (text->nonce) _plug_free_string(utils,&(text->nonce)); + if (text->salt) utils->free(text->salt); + if (text->cbindingname != NULL) { + utils->free(text->cbindingname); + text->cbindingname = NULL; + } + if (text->gs2_header != NULL) { + utils->free(text->gs2_header); + text->gs2_header = NULL; + } + + utils->free(text); +} + +static sasl_server_plug_t scram_server_plugins[] = +{ +#ifdef HAVE_SHA512 + { + "SCRAM-SHA-512", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(512) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOACTIVE + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + "SHA512", /* glob_context */ + &scram_server_mech_new, /* mech_new */ + &scram_server_mech_step, /* mech_step */ + &scram_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + &scram_setpass, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech avail */ + NULL /* spare */ + }, + { + "SCRAM-SHA-384", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(384) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOACTIVE + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + "SHA384", /* glob_context */ + &scram_server_mech_new, /* mech_new */ + &scram_server_mech_step, /* mech_step */ + &scram_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + &scram_setpass, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech avail */ + NULL /* spare */ + }, + { + "SCRAM-SHA-256", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(256) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOACTIVE + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + "SHA256", /* glob_context */ + &scram_server_mech_new, /* mech_new */ + &scram_server_mech_step, /* mech_step */ + &scram_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + &scram_setpass, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech avail */ + NULL /* spare */ + }, + { + "SCRAM-SHA-224", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(224) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOACTIVE + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + "SHA224", /* glob_context */ + &scram_server_mech_new, /* mech_new */ + &scram_server_mech_step, /* mech_step */ + &scram_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + &scram_setpass, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech avail */ + NULL /* spare */ + }, +#endif + { + "SCRAM-SHA-1", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(160) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOACTIVE + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + "SHA1", /* glob_context */ + &scram_server_mech_new, /* mech_new */ + &scram_server_mech_step, /* mech_step */ + &scram_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + &scram_setpass, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech avail */ + NULL /* spare */ + } +}; + +int scram_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { + SETERROR( utils, "SCRAM-SHA-* version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = scram_server_plugins; +#ifdef HAVE_SHA512 + *plugcount = 5; +#else + *plugcount = 1; +#endif + utils->rand(utils->rpool, (char *)g_salt_key, SALT_SIZE); + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + int state; + + const EVP_MD *md; /* underlying MDA */ + + sasl_secret_t *password; /* user password */ + unsigned int free_password; /* set if we need to free the password */ + + char * gs2_header; + size_t gs2_header_length; + char * out_buf; + unsigned out_buf_len; + char * auth_message; + size_t auth_message_len; + char * nonce; + /* in binary form */ + char * salt; + size_t salt_len; + unsigned int iteration_count; + char SaltedPassword[EVP_MAX_MD_SIZE]; + + int cb_flags; +} client_context_t; + +static int scram_client_mech_new(void *glob_context, + sasl_client_params_t *params, + void **conn_context) +{ + client_context_t *text; + + /* holds state are in */ + text = params->utils->malloc(sizeof(client_context_t)); + if (text == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(client_context_t)); + + text->md = EVP_get_digestbyname((const char *) glob_context); + + *conn_context = text; + + return SASL_OK; +} + +static int +scram_client_mech_step1(client_context_t *text, + sasl_client_params_t *params, + const char *serverin __attribute__((unused)), + unsigned serverinlen __attribute__((unused)), + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + const char *authid = NULL; + const char *userid = NULL; + int user_result = SASL_OK; + int auth_result = SASL_OK; + int pass_result = SASL_OK; + int result; + size_t maxsize; + char * encoded_authcid; + char * freeme = NULL; + char * freeme2 = NULL; + char channel_binding_state = 'n'; + const char * channel_binding_name = NULL; + char * encoded_authorization_id = NULL; + const char *scram_sasl_mech = scram_sasl_mech_name(EVP_MD_size(text->md)); + + /* check if sec layer strong enough */ + if (params->props.min_ssf > params->external_ssf) { + params->utils->seterror(params->utils->conn, 0, + "SSF requested of %s plugin", + scram_sasl_mech); + return SASL_TOOWEAK; + } + + /* try to get the userid */ + if (oparams->authid == NULL) { + auth_result=_plug_get_authid(params->utils, &authid, prompt_need); + + if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) + return auth_result; + } + + /* try to get the userid */ + if (oparams->user == NULL) { + user_result = _plug_get_userid(params->utils, &userid, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { + return user_result; + } + } + + /* try to get the password */ + if (text->password == NULL) { + pass_result = _plug_get_password(params->utils, + &text->password, + &text->free_password, + prompt_need); + if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) { + return pass_result; + } + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if ((auth_result == SASL_INTERACT) || + (user_result == SASL_INTERACT) || + (pass_result == SASL_INTERACT)) { + /* make the prompt list */ + result = + _plug_make_prompts(params->utils, + prompt_need, + user_result == SASL_INTERACT ? + "Please enter your authorization name" : NULL, + NULL, + auth_result == SASL_INTERACT ? + "Please enter your authentication name" : NULL, + NULL, + pass_result == SASL_INTERACT ? + "Please enter your password" : NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + + if (result != SASL_OK) { + goto cleanup; + } + + return SASL_INTERACT; + } + + if (!text->password) { + PARAMERROR(params->utils); + return SASL_BADPARAM; + } + + if (oparams->authid == NULL) { + if (!userid || !*userid) { + result = params->canon_user(params->utils->conn, + authid, + 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + oparams); + } + else { + result = params->canon_user(params->utils->conn, + authid, + 0, + SASL_CU_AUTHID, + oparams); + if (result != SASL_OK) { + goto cleanup; + } + + result = params->canon_user(params->utils->conn, + userid, + 0, + SASL_CU_AUTHZID, + oparams); + } + if (result != SASL_OK) { + goto cleanup; + } + } + + switch (params->cbindingdisp) { + case SASL_CB_DISP_NONE: + text->cb_flags = SCRAM_CB_FLAG_N; + channel_binding_state = 'n'; + break; + case SASL_CB_DISP_USED: + if (!SASL_CB_PRESENT(params)) { + result = SASL_BADPARAM; + goto cleanup; + } + channel_binding_name = params->cbinding->name; + text->cb_flags = SCRAM_CB_FLAG_P; + channel_binding_state = 'p'; + break; + case SASL_CB_DISP_WANT: + text->cb_flags = SCRAM_CB_FLAG_Y; + channel_binding_state = 'y'; + break; + } + + text->nonce = params->utils->malloc (NONCE_SIZE + 1); + + if (text->nonce == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + if (create_nonce(params->utils, + text->nonce, + NONCE_SIZE + 1) == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + if (userid != NULL && *userid != '\0') { + result = encode_saslname (oparams->user, + (const char **) &encoded_authorization_id, + &freeme2); + + if (result != SASL_OK) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + } + + result = encode_saslname (oparams->authid, + (const char **) &encoded_authcid, + &freeme); + if (result != SASL_OK) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + maxsize = strlen("p=,a=,n=,r=") + + ((channel_binding_name != NULL) ? strlen(channel_binding_name) : 0) + + ((encoded_authorization_id != NULL) ? strlen(encoded_authorization_id) : 0) + + strlen(encoded_authcid) + + strlen(text->nonce); + result = _plug_buf_alloc(params->utils, + &(text->out_buf), + &(text->out_buf_len), + (unsigned) maxsize + 1); + if (result != SASL_OK) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + snprintf(text->out_buf, + maxsize + 1, + "%c%s%s,%s%s,", + channel_binding_state, + (channel_binding_name != NULL) ? "=" : "", + (channel_binding_name != NULL) ? channel_binding_name : "", + (encoded_authorization_id != NULL) ? "a=" : "", + (encoded_authorization_id != NULL) ? encoded_authorization_id : ""); + + text->gs2_header_length = strlen(text->out_buf); + _plug_strdup(params->utils, text->out_buf, &text->gs2_header, NULL); + + sprintf(text->out_buf + text->gs2_header_length, + "n=%s,r=%s", + encoded_authcid, + text->nonce); + + /* Save the copy of the client-first-message */ + + /* Need to skip the GS2 prefix here */ + _plug_strdup(params->utils, + text->out_buf + text->gs2_header_length, + &text->auth_message, + NULL); + if (text->auth_message == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + text->auth_message_len = strlen(text->auth_message); + + *clientout = text->out_buf; + *clientoutlen = (unsigned) strlen(*clientout); + + result = SASL_CONTINUE; + +cleanup: + if (freeme != NULL) _plug_free_string(params->utils, &freeme); + if (freeme2 != NULL) _plug_free_string(params->utils, &freeme2); + + return result; +} + +static int +scram_client_mech_step2(client_context_t *text, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need __attribute__((unused)), + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams __attribute__((unused))) +{ + char * p; + char * nonce; + size_t server_nonce_len; + char * base64_salt = NULL; + size_t base64_salt_len; + unsigned exact_salt_len; + char * counter; + char * end; + char * inbuf = NULL; + size_t estimated_response_len; + size_t length_no_proof; + char * full_auth_message; + size_t cb_bin_length; + size_t channel_binding_data_len = 0; + size_t cb_encoded_length; + const char * channel_binding_data = NULL; + char * cb_encoded = NULL; + char * cb_bin = NULL; + int result; + char ClientKey[EVP_MAX_MD_SIZE]; + char StoredKey[EVP_MAX_MD_SIZE]; + char ClientSignature[EVP_MAX_MD_SIZE]; + char ClientProof[EVP_MAX_MD_SIZE]; + char * client_proof = NULL; + size_t client_proof_len; + unsigned int hash_len = 0; + size_t k, hash_size = EVP_MD_size(text->md); + const char *scram_sasl_mech = scram_sasl_mech_name(hash_size); + + if (serverinlen == 0) { + params->utils->seterror(params->utils->conn, 0, + "%s input expected", scram_sasl_mech); + return SASL_BADPROT; + } + + /* [reserved-mext ","] nonce "," salt "," iteration-count ["," extensions] */ + + if (serverinlen < 3 || serverin[1] != '=') { + params->utils->seterror(params->utils->conn, 0, + "Invalid %s input", scram_sasl_mech); + return SASL_BADPROT; + } + + if (serverin[0] == 'm') { + params->utils->seterror(params->utils->conn, 0, + "Unsupported mandatory extension to %s", + scram_sasl_mech); + return SASL_BADPROT; + } + + if (serverin[0] != 'r') { + params->utils->seterror(params->utils->conn, 0, + "Nonce (r=) expected in %s input", + scram_sasl_mech); + return SASL_BADPROT; + } + + inbuf = params->utils->malloc (serverinlen + 1); + if (inbuf == NULL) { + MEMERROR( params->utils ); + return SASL_NOMEM; + } + + memcpy(inbuf, serverin, serverinlen); + inbuf[serverinlen] = 0; + + if (strlen(inbuf) != serverinlen) { + params->utils->seterror(params->utils->conn, 0, + "NULs found in %s input", scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + nonce = inbuf + 2; + p = strchr (nonce, ','); + + /* MUST be followed by a salt */ + if (p == NULL) { + params->utils->seterror(params->utils->conn, 0, + "Salt expected after the nonce in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + *p = '\0'; + p++; + + if (strncmp(p, "s=", 2) != 0) { + params->utils->seterror(params->utils->conn, 0, + "Salt expected after the nonce in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + p += 2; + base64_salt = p; + + p = strchr (base64_salt, ','); + + /* MUST be followed by an iteration-count */ + if (p == NULL) { + params->utils->seterror(params->utils->conn, 0, + "iteration-count expected after the salt in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + *p = '\0'; + p++; + + if (strncmp(p, "i=", 2) != 0) { + params->utils->seterror(params->utils->conn, 0, + "iteration-count expected after the salt in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + p += 2; + counter = p; + p = strchr (counter, ','); + + if (p == NULL) { + p = counter + strlen(counter); + } else { + *p = '\0'; + } + + errno = 0; + text->iteration_count = strtoul(counter, &end, 10); + if (counter == end || *end != '\0' || errno != 0) { + params->utils->seterror(params->utils->conn, 0, + "Invalid iteration-count in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (text->iteration_count < MIN_ITERATION_COUNTER) { + } + + if (text->iteration_count > MAX_ITERATION_COUNTER) { + SETERROR(params->utils, "iteration-count is too big, refusing to compute"); + result = SASL_BADPROT; + goto cleanup; + } + + /* The client MUST verify that the initial part of the nonce + used in subsequent messages is the same as the nonce it + initially specified. */ + server_nonce_len = strlen(nonce); + + if (server_nonce_len <= NONCE_SIZE || + strncmp(nonce, text->nonce, NONCE_SIZE) != 0) { + SETERROR(params->utils, "The nonce received from the server doesn't start from the nonce sent by the client"); + result = SASL_BADPROT; + goto cleanup; + } + + /* Now we can forget about our nonce */ + params->utils->free(text->nonce); + + _plug_strdup(params->utils, nonce, &text->nonce, NULL); + + if (text->nonce == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + /* base64 decode salt */ + base64_salt_len = strlen(base64_salt); + + if (base64_salt_len == 0) { + SETERROR(params->utils, "The salt can't be empty"); + result = SASL_BADPROT; + goto cleanup; + } + + if (base64_salt_len % 4 != 0) { + SETERROR(params->utils, "Invalid base64 encoding of the salt"); + result = SASL_BADPROT; + goto cleanup; + } + + text->salt_len = base64_salt_len / 4 * 3; + + text->salt = (char *) params->utils->malloc(text->salt_len + 1); + if (text->salt == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + if (params->utils->decode64(base64_salt, + (unsigned int)base64_salt_len, + text->salt, + (unsigned int)text->salt_len + 1, + &exact_salt_len) != SASL_OK) { + params->utils->seterror(params->utils->conn, 0, + "Invalid base64 encoding of the salt in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + text->salt_len = exact_salt_len; + + /* Now we generate client response */ + + if (text->gs2_header[0] == 'p') { + + if (params->cbinding == NULL) { + result = SASL_FAIL; + goto cleanup; + } + + channel_binding_data = (const char *) params->cbinding->data; + channel_binding_data_len = params->cbinding->len; + } + + cb_bin_length = text->gs2_header_length + + ((channel_binding_data != NULL) ? channel_binding_data_len : 0); + cb_encoded_length = (cb_bin_length / 3 * 4) + ((cb_bin_length % 3) ? 4 : 0); + + if (channel_binding_data != NULL) { + cb_bin = (char *) params->utils->malloc(cb_bin_length + 1); + if (cb_bin == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + memcpy(cb_bin, text->gs2_header, text->gs2_header_length); + memcpy(cb_bin + text->gs2_header_length, channel_binding_data, channel_binding_data_len); + } + + cb_encoded = (char *) params->utils->malloc(cb_encoded_length + 1); + if (cb_encoded == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + /* + * Returns SASL_OK on success, SASL_BUFOVER if result won't fit + */ + if (params->utils->encode64((cb_bin != NULL) ? cb_bin : text->gs2_header, + (unsigned int)cb_bin_length, + cb_encoded, + (unsigned int)cb_encoded_length + 1, + NULL) != SASL_OK) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + cb_encoded[cb_encoded_length] = '\0'; + + client_proof_len = hash_size / 3 * 4 + ((hash_size % 3) ? 4 : 0); + estimated_response_len = strlen(cb_encoded)+ + strlen(text->nonce)+ + client_proof_len + + strlen("c=,r=,p="); + result = _plug_buf_alloc(params->utils, + &(text->out_buf), + &(text->out_buf_len), + (unsigned) estimated_response_len + 1); + if (result != SASL_OK) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + /* channel-binding "," nonce ["," extensions] */ + sprintf(text->out_buf, + "c=%s,r=%s", + cb_encoded, + text->nonce); + + length_no_proof = strlen(text->out_buf); + + /* Build AuthMessage */ + full_auth_message = params->utils->realloc(text->auth_message, + text->auth_message_len + 1 + + serverinlen + 1 + + length_no_proof + 1); + if (full_auth_message == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + text->auth_message = full_auth_message; + + text->auth_message[text->auth_message_len] = ','; + memcpy(text->auth_message + text->auth_message_len + 1, serverin, serverinlen); + text->auth_message[text->auth_message_len + 1 + serverinlen] = ','; + memcpy(text->auth_message + text->auth_message_len + 1 + serverinlen + 1, + text->out_buf, + length_no_proof); + text->auth_message_len += serverinlen + 2 + length_no_proof; + text->auth_message[text->auth_message_len] = '\0'; + + /* Calculate ClientProof */ + + /* SaltedPassword := Hi(password, salt) */ + Hi (params->utils, + text->md, + (const char *) text->password->data, + text->password->len, + text->salt, + text->salt_len, + text->iteration_count, + text->SaltedPassword); + + PRINT_HASH ("SaltedPassword", text->SaltedPassword, hash_size); + + /* ClientKey := HMAC(SaltedPassword, "Client Key") */ + if (HMAC(text->md, + (const unsigned char *) text->SaltedPassword, + hash_size, + (const unsigned char *) CLIENT_KEY_CONSTANT, + CLIENT_KEY_CONSTANT_LEN, + (unsigned char *)ClientKey, + &hash_len) == NULL) { + params->utils->seterror(params->utils->conn,0, + "HMAC-%s call failed", scram_sasl_mech+6); + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + PRINT_HASH ("ClientKey", ClientKey, hash_size); + + /* StoredKey := H(ClientKey) */ + if (EVP_Digest((const unsigned char *) ClientKey, hash_size, + (unsigned char *) StoredKey, NULL, text->md, NULL) == 0) { + params->utils->seterror(params->utils->conn,0, + "%s call failed", scram_sasl_mech+6); + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + PRINT_HASH ("StoredKey", StoredKey, hash_size); + + /* ClientSignature := HMAC(StoredKey, AuthMessage) */ + if (HMAC(text->md, + (const unsigned char *)StoredKey, + hash_size, + (const unsigned char *) text->auth_message, + (int)text->auth_message_len, + (unsigned char *)ClientSignature, + &hash_len) == NULL) { + params->utils->seterror(params->utils->conn,0, + "HMAC-%s call failed", scram_sasl_mech+6); + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + PRINT_HASH ("ClientSignature", ClientSignature, hash_size); + + /* ClientProof := ClientKey XOR ClientSignature */ + for (k = 0; k < hash_size; k++) { + ClientProof[k] = ClientKey[k] ^ ClientSignature[k]; + } + + PRINT_HASH ("ClientProof", ClientProof, hash_size); + + /* base64-encode ClientProof */ + client_proof = (char *) params->utils->malloc(client_proof_len + 1); + if (client_proof == NULL) { + MEMERROR( params->utils ); + result = SASL_NOMEM; + goto cleanup; + } + + result = params->utils->encode64(ClientProof, + hash_size, + client_proof, + (unsigned int)client_proof_len + 1, + NULL); + + if (result != SASL_OK) { + goto cleanup; + } + + client_proof[client_proof_len] = '\0'; + + sprintf(text->out_buf + length_no_proof, + ",p=%s", + client_proof); + + *clientout = text->out_buf; + *clientoutlen = (unsigned) strlen(text->out_buf); + + result = SASL_CONTINUE; + +cleanup: + if (inbuf != NULL) { + params->utils->free(inbuf); + } + + if (client_proof != NULL) { + params->utils->free(client_proof); + } + + if (cb_encoded != NULL) { + params->utils->free(cb_encoded); + } + + if (cb_bin != NULL) { + params->utils->free(cb_bin); + } + + return result; +} + + +static int +scram_client_mech_step3(client_context_t *text, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need __attribute__((unused)), + const char **clientout __attribute__((unused)), + unsigned *clientoutlen __attribute__((unused)), + sasl_out_params_t *oparams) +{ + char * p; + int result; + size_t server_proof_len; + unsigned exact_server_proof_len; + char DecodedServerProof[EVP_MAX_MD_SIZE + 1]; + char ServerKey[EVP_MAX_MD_SIZE]; + char ServerSignature[EVP_MAX_MD_SIZE]; + unsigned int hash_len = 0; + size_t k, hash_size = EVP_MD_size(text->md); + const char *scram_sasl_mech = scram_sasl_mech_name(hash_size); + + if (serverinlen < 3) { + params->utils->seterror(params->utils->conn, 0, + "Invalid %s input expected", + scram_sasl_mech); + return SASL_BADPROT; + } + + /* Expecting: 'verifier ["," extensions]' */ + + if (strncmp(serverin, "v=", 2) != 0) { + params->utils->seterror(params->utils->conn, 0, + "ServerSignature expected in %s input", + scram_sasl_mech); + return SASL_BADPROT; + } + + /* Use memchr instead of the original strchr as there is no guarantee that + the input data is NUL terminated */ + p = memchr (serverin + 2, ',', serverinlen - 2); + if (p != NULL) { + server_proof_len = p - (serverin + 2) - 1; + } else { + server_proof_len = serverinlen - 2; + } + + if (params->utils->decode64(serverin + 2, /* ServerProof */ + (unsigned int)server_proof_len, + DecodedServerProof, + hash_size + 1, + &exact_server_proof_len) != SASL_OK) { + params->utils->seterror(params->utils->conn, 0, + "Invalid base64 encoding of the server proof in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + if (exact_server_proof_len != hash_size) { + params->utils->seterror(params->utils->conn, 0, + "Invalid server proof (truncated) in %s input", + scram_sasl_mech); + result = SASL_BADPROT; + goto cleanup; + } + + /* ServerKey := HMAC(SaltedPassword, "Server Key") */ + if (HMAC(text->md, + (const unsigned char *)text->SaltedPassword, + hash_size, + (const unsigned char *) SERVER_KEY_CONSTANT, + SERVER_KEY_CONSTANT_LEN, + (unsigned char *)ServerKey, + &hash_len) == NULL) { + params->utils->seterror(params->utils->conn,0, + "HMAC-%s call failed", scram_sasl_mech+6); + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + /* ServerSignature := HMAC(ServerKey, AuthMessage) */ + if (HMAC(text->md, + (const unsigned char *)ServerKey, + hash_size, + (const unsigned char *) text->auth_message, + (int)text->auth_message_len, + (unsigned char *)ServerSignature, + &hash_len) == NULL) { + params->utils->seterror(params->utils->conn,0, + "HMAC-%s call failed", scram_sasl_mech+6); + result = SASL_SCRAM_INTERNAL; + goto cleanup; + } + + for (k = 0; k < hash_size; k++) { + if (DecodedServerProof[k] != ServerSignature[k]) { + SETERROR(params->utils, "ServerSignature mismatch"); + result = SASL_BADAUTH; + goto cleanup; + } + } + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + result = SASL_OK; + +cleanup: + return result; +} + +static int scram_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + int result = SASL_FAIL; + client_context_t *text = (client_context_t *) conn_context; + const char *scram_sasl_mech = scram_sasl_mech_name(EVP_MD_size(text->md)); + + *clientout = NULL; + *clientoutlen = 0; + + /* this should be well more than is ever needed */ + if (serverinlen > MAX_SERVERIN_LEN) { + params->utils->seterror(params->utils->conn, 0, + "%s input longer than " STRINGIZE((MAX_SERVERIN_LEN)) " bytes", + scram_sasl_mech); + return SASL_BADPROT; + } + + switch (text->state) { + case 0: + result = scram_client_mech_step1(text, + params, + serverin, + serverinlen, + prompt_need, + clientout, + clientoutlen, + oparams); + break; + + case 1: + result = scram_client_mech_step2(text, + params, + serverin, + serverinlen, + prompt_need, + clientout, + clientoutlen, + oparams); + break; + + case 2: + result = scram_client_mech_step3(text, + params, + serverin, + serverinlen, + prompt_need, + clientout, + clientoutlen, + oparams); + break; + + default: /* should never get here */ + params->utils->log(NULL, SASL_LOG_ERR, + "Invalid %s client step %d\n", + scram_sasl_mech, text->state); + return SASL_FAIL; + } + + if (result != SASL_INTERACT) { + text->state++; + } + return result; +} + +static void scram_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + client_context_t *text = (client_context_t *) conn_context; + + if (!text) return; + + /* get rid of all sensitive info */ + if (text->free_password) { + _plug_free_secret(utils, &text->password); + text->free_password = 0; + } + + if (text->gs2_header) { + utils->free(text->gs2_header); + text->gs2_header = NULL; + } + + if (text->out_buf) { + utils->free(text->out_buf); + text->out_buf = NULL; + } + + if (text->auth_message) _plug_free_string(utils,&(text->auth_message)); + if (text->nonce) _plug_free_string(utils,&(text->nonce)); + if (text->salt) utils->free(text->salt); + + utils->free(text); +} + +static sasl_client_plug_t scram_client_plugins[] = +{ +#ifdef HAVE_SHA512 + { + "SCRAM-SHA-512", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(512) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NOACTIVE + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + NULL, /* required_prompts */ + "SHA512", /* glob_context */ + &scram_client_mech_new, /* mech_new */ + &scram_client_mech_step, /* mech_step */ + &scram_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + }, + { + "SCRAM-SHA-384", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(384) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NOACTIVE + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + NULL, /* required_prompts */ + "SHA384", /* glob_context */ + &scram_client_mech_new, /* mech_new */ + &scram_client_mech_step, /* mech_step */ + &scram_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + }, + { + "SCRAM-SHA-256", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(256) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NOACTIVE + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + NULL, /* required_prompts */ + "SHA256", /* glob_context */ + &scram_client_mech_new, /* mech_new */ + &scram_client_mech_step, /* mech_step */ + &scram_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + }, + { + "SCRAM-SHA-224", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(224) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NOACTIVE + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + NULL, /* required_prompts */ + "SHA224", /* glob_context */ + &scram_client_mech_new, /* mech_new */ + &scram_client_mech_step, /* mech_step */ + &scram_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + }, +#endif + { + "SCRAM-SHA-1", /* mech_name */ + 0, /* max_ssf */ + SASL_SET_HASH_STRENGTH_BITS(160) | + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NOACTIVE + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_ALLOWS_PROXY + | SASL_FEAT_SUPPORTS_HTTP + | SASL_FEAT_CHANNEL_BINDING, /* features */ + NULL, /* required_prompts */ + "SHA1", /* glob_context */ + &scram_client_mech_new, /* mech_new */ + &scram_client_mech_step, /* mech_step */ + &scram_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int scram_client_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR( utils, "SCRAM-SHA-* version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = scram_client_plugins; +#ifdef HAVE_SHA512 + *plugcount = 5; +#else + *plugcount = 1; +#endif + + return SASL_OK; +} diff --git a/contrib/libs/sasl/sasldb/db_none.c b/contrib/libs/sasl/sasldb/db_none.c new file mode 100644 index 0000000000..12eaedb2bf --- /dev/null +++ b/contrib/libs/sasl/sasldb/db_none.c @@ -0,0 +1,103 @@ +/* db_none.c--provides linkage for systems which lack a backend db lib + * Rob Siemborski + * Rob Earhart + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include "sasldb.h" + +/* This just exists to provide these symbols on systems where configure + * couldn't find a database library (or the user says we do not want one). */ +int _sasldb_getdata(const sasl_utils_t *utils, + sasl_conn_t *conn, + const char *authid __attribute__((unused)), + const char *realm __attribute__((unused)), + const char *propName __attribute__((unused)), + char *out __attribute__((unused)), + const size_t max_out __attribute__((unused)), + size_t *out_len __attribute__((unused))) +{ + if(conn) utils->seterror(conn, 0, "No Database Driver"); + return SASL_FAIL; +} + +int _sasldb_putdata(const sasl_utils_t *utils, + sasl_conn_t *conn, + const char *authid __attribute__((unused)), + const char *realm __attribute__((unused)), + const char *propName __attribute__((unused)), + const char *data __attribute__((unused)), + size_t data_len __attribute__((unused))) +{ + if(conn) utils->seterror(conn, 0, "No Database Driver"); + return SASL_FAIL; +} + +int _sasl_check_db(const sasl_utils_t *utils, + sasl_conn_t *conn) +{ + if(conn) utils->seterror(conn, 0, "No Database Driver"); + return SASL_FAIL; +} + +sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils, + sasl_conn_t *conn) +{ + if(conn) utils->seterror(conn, 0, "No Database Driver"); + return NULL; +} + +int _sasldb_getnextkey(const sasl_utils_t *utils __attribute__((unused)), + sasldb_handle handle __attribute__((unused)), + char *out __attribute__((unused)), + const size_t max_out __attribute__((unused)), + size_t *out_len __attribute__((unused))) +{ + return SASL_FAIL; +} + +int _sasldb_releasekeyhandle(const sasl_utils_t *utils __attribute__((unused)), + sasldb_handle handle __attribute__((unused))) +{ + return SASL_FAIL; +} diff --git a/contrib/libs/sasl/sasldb/sasldb.h b/contrib/libs/sasl/sasldb/sasldb.h new file mode 100644 index 0000000000..8068a8b019 --- /dev/null +++ b/contrib/libs/sasl/sasldb/sasldb.h @@ -0,0 +1,134 @@ +/* sasldb.h - SASLdb library header + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SASLDB_H +#define SASLDB_H + +#include "sasl.h" +#include "saslplug.h" + +/* + * Note that some of these require a sasl_conn_t in order for + * the getcallback stuff to work correctly. This is great for + * when they are called from a plugin or the library but makes + * for much wierdness when an otherwise non-sasl application needs + * to make use of this functionality. + */ + +int _sasldb_getdata(const sasl_utils_t *utils, + sasl_conn_t *conn, + const char *authid, + const char *realm, + const char *propName, + char *out, const size_t max_out, size_t *out_len); + +/* pass NULL for data to delete it */ +int _sasldb_putdata(const sasl_utils_t *utils, + sasl_conn_t *conn, + const char *authid, + const char *realm, + const char *propName, + const char *data, size_t data_len); + +/* Should be run before any db access is attempted */ +LIBSASL_API int _sasl_check_db(const sasl_utils_t *utils, + sasl_conn_t *conn); + +/* These allow iterating through the keys of the database */ +typedef void* sasldb_handle; + +typedef int (* sasldb_list_callback_t) (const char *authid, + const char *realm, + const char *property, + void *rock); + +LIBSASL_API sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils, + sasl_conn_t *conn); +LIBSASL_API int _sasldb_getnextkey(const sasl_utils_t *utils, + sasldb_handle handle, char *out, + const size_t max_out, size_t *out_len); +LIBSASL_API int _sasldb_releasekeyhandle(const sasl_utils_t *utils, + sasldb_handle handle); + +LIBSASL_API int _sasldb_listusers(const sasl_utils_t *utils, + sasl_conn_t *context, + sasldb_list_callback_t callback, + void *callback_rock); + +#if defined(KEEP_DB_OPEN) +void sasldb_auxprop_free (void *glob_context, const sasl_utils_t *utils); +#else +#define sasldb_auxprop_free NULL +#endif + +/* The rest are implemented in allockey.c and individual drivers need not + * do so */ +/* These two are aliases for getdata/putdata */ +int _sasldb_getsecret(const sasl_utils_t *utils, + sasl_conn_t *context, + const char *auth_identity, + const char *realm, + sasl_secret_t ** secret); + +int _sasldb_putsecret(const sasl_utils_t *utils, + sasl_conn_t *context, + const char *auth_identity, + const char *realm, + const sasl_secret_t * secret); + +LIBSASL_API int _sasldb_parse_key(const char *key, const size_t key_len, + char *authid, const size_t max_authid, + char *realm, const size_t max_realm, + char *propName, const size_t max_propname); + +/* This function is internal, but might be useful to have around */ +int _sasldb_alloc_key(const sasl_utils_t *utils, + const char *auth_identity, + const char *realm, + const char *propName, + char **key, + size_t *key_len); + +#endif /* SASLDB_H */ diff --git a/contrib/libs/sasl/ya.make b/contrib/libs/sasl/ya.make new file mode 100644 index 0000000000..bc4592640e --- /dev/null +++ b/contrib/libs/sasl/ya.make @@ -0,0 +1,68 @@ +# Generated by devtools/yamaker from nixpkgs 22.11. + +LIBRARY() + +LICENSE( + BSD-4-Clause-UC AND + Cmu-Computing-Services AND + IBM-pibs AND + LicenseRef-scancode-paul-mackerras-binary AND + RSA-MD +) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +VERSION(2.1.28) + +ORIGINAL_SOURCE(https://github.com/cyrusimap/cyrus-sasl/archive/cyrus-sasl-2.1.28.tar.gz) + +PEERDIR( + contrib/libs/openssl +) + +ADDINCL( + contrib/libs/sasl + contrib/libs/sasl/common + contrib/libs/sasl/include + contrib/libs/sasl/lib + contrib/libs/sasl/plugins + contrib/libs/sasl/sasldb +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DCONFIGDIR=\"/tmp/yamaker/sasl/out/lib/sasl2:/var/empty/cyrus-sasl-2.1.28/etc/sasl2\" + -DHAVE_CONFIG_H + -DOBSOLETE_CRAM_ATTR=1 + -DOBSOLETE_DIGEST_ATTR=1 + -DPLUGINDIR=\"/tmp/yamaker/sasl/out/lib/sasl2\" +) + +SRCS( + common/plugin_common.c + lib/auxprop.c + lib/canonusr.c + lib/checkpw.c + lib/client.c + lib/common.c + lib/config.c + lib/dlopen.c + lib/external.c + lib/md5.c + lib/saslutil.c + lib/server.c + lib/seterror.c + plugins/anonymous.c + plugins/cram.c + plugins/digestmd5.c + plugins/otp.c + plugins/plain.c + plugins/sasldb.c + plugins/scram.c + sasldb/db_none.c +) + +END() |