1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-07-27 19:21:47 +02:00

Initial preparations for CURL and Discord integration.

This commit is contained in:
Sandu Liviu Catalin
2021-01-27 07:27:48 +02:00
parent 8257eb61d6
commit 95705e87c8
1751 changed files with 440547 additions and 854 deletions
CMakeLists.txt
module
Base
CMakeLists.txt
Core
Entity
Library
Logger.cpp
Misc
Register.cppSqBase.hpp
Vendor
AES256
ASIO
COPYINGINSTALLLICENSE_1_0.txtMakefile.amREADMEasio.manifestautogen.shboost_asio.manifestboostify.plconfigure.ac
include
Makefile.amasio.hpp
asio
any_io_executor.hppassociated_allocator.hppassociated_executor.hppasync_result.hppawaitable.hppbasic_datagram_socket.hppbasic_deadline_timer.hppbasic_io_object.hppbasic_raw_socket.hppbasic_seq_packet_socket.hppbasic_serial_port.hppbasic_signal_set.hppbasic_socket.hppbasic_socket_acceptor.hppbasic_socket_iostream.hppbasic_socket_streambuf.hppbasic_stream_socket.hppbasic_streambuf.hppbasic_streambuf_fwd.hppbasic_waitable_timer.hppbind_executor.hppbuffer.hppbuffered_read_stream.hppbuffered_read_stream_fwd.hppbuffered_stream.hppbuffered_stream_fwd.hppbuffered_write_stream.hppbuffered_write_stream_fwd.hppbuffers_iterator.hppco_spawn.hppcompletion_condition.hppcompose.hppconnect.hppcoroutine.hppdeadline_timer.hppdefer.hppdetached.hpp
detail
array.hpparray_fwd.hppassert.hppatomic_count.hppbase_from_completion_cond.hppbind_handler.hppblocking_executor_op.hppbuffer_resize_guard.hppbuffer_sequence_adapter.hppbuffered_stream_storage.hppbulk_executor_op.hppcall_stack.hppchrono.hppchrono_time_traits.hppcompletion_handler.hppconcurrency_hint.hppconditionally_enabled_event.hppconditionally_enabled_mutex.hppconfig.hppconsuming_buffers.hppcstddef.hppcstdint.hppdate_time_fwd.hppdeadline_timer_service.hppdependent_type.hppdescriptor_ops.hppdescriptor_read_op.hppdescriptor_write_op.hppdev_poll_reactor.hppepoll_reactor.hppevent.hppeventfd_select_interrupter.hppexecutor_function.hppexecutor_op.hppfd_set_adapter.hppfenced_block.hppfunctional.hppfuture.hppgcc_arm_fenced_block.hppgcc_hppa_fenced_block.hppgcc_sync_fenced_block.hppgcc_x86_fenced_block.hppglobal.hpphandler_alloc_helpers.hpphandler_cont_helpers.hpphandler_invoke_helpers.hpphandler_tracking.hpphandler_type_requirements.hpphandler_work.hpphash_map.hpp
impl
io_control.hppio_object_impl.hppis_buffer_sequence.hppis_executor.hppkeyword_tss_ptr.hppkqueue_reactor.hpplimits.hpplocal_free_on_block_exit.hppmacos_fenced_block.hppmemory.hppmutex.hppnon_const_lvalue.hppnoncopyable.hppnull_event.hppnull_fenced_block.hppnull_global.hppnull_mutex.hppnull_reactor.hppnull_signal_blocker.hppnull_socket_service.hppnull_static_mutex.hppnull_thread.hppnull_tss_ptr.hppobject_pool.hppold_win_sdk_compat.hppop_queue.hppoperation.hpppipe_select_interrupter.hpppop_options.hppposix_event.hppposix_fd_set_adapter.hppposix_global.hppposix_mutex.hppposix_signal_blocker.hppposix_static_mutex.hppposix_thread.hppposix_tss_ptr.hpppush_options.hppreactive_descriptor_service.hppreactive_null_buffers_op.hppreactive_serial_port_service.hppreactive_socket_accept_op.hppreactive_socket_connect_op.hppreactive_socket_recv_op.hppreactive_socket_recvfrom_op.hppreactive_socket_recvmsg_op.hppreactive_socket_send_op.hppreactive_socket_sendto_op.hppreactive_socket_service.hppreactive_socket_service_base.hppreactive_wait_op.hppreactor.hppreactor_fwd.hppreactor_op.hppreactor_op_queue.hpprecycling_allocator.hppregex_fwd.hppresolve_endpoint_op.hppresolve_op.hppresolve_query_op.hppresolver_service.hppresolver_service_base.hppscheduler.hppscheduler_operation.hppscheduler_thread_info.hppscoped_lock.hppscoped_ptr.hppselect_interrupter.hppselect_reactor.hppservice_registry.hppsignal_blocker.hppsignal_handler.hppsignal_init.hppsignal_op.hppsignal_set_service.hppsocket_holder.hppsocket_ops.hppsocket_option.hppsocket_select_interrupter.hppsocket_types.hppsolaris_fenced_block.hppsource_location.hppstatic_mutex.hppstd_event.hppstd_fenced_block.hppstd_global.hppstd_mutex.hppstd_static_mutex.hppstd_thread.hppstrand_executor_service.hppstrand_service.hppstring_view.hppthread.hppthread_context.hppthread_group.hppthread_info_base.hppthrow_error.hppthrow_exception.hpptimer_queue.hpptimer_queue_base.hpptimer_queue_ptime.hpptimer_queue_set.hpptimer_scheduler.hpptimer_scheduler_fwd.hpptss_ptr.hpptype_traits.hppvariadic_templates.hppwait_handler.hppwait_op.hppwin_event.hppwin_fd_set_adapter.hppwin_fenced_block.hppwin_global.hppwin_iocp_handle_read_op.hppwin_iocp_handle_service.hppwin_iocp_handle_write_op.hppwin_iocp_io_context.hppwin_iocp_null_buffers_op.hppwin_iocp_operation.hppwin_iocp_overlapped_op.hppwin_iocp_overlapped_ptr.hppwin_iocp_serial_port_service.hppwin_iocp_socket_accept_op.hppwin_iocp_socket_connect_op.hppwin_iocp_socket_recv_op.hppwin_iocp_socket_recvfrom_op.hppwin_iocp_socket_recvmsg_op.hppwin_iocp_socket_send_op.hppwin_iocp_socket_service.hppwin_iocp_socket_service_base.hppwin_iocp_thread_info.hppwin_iocp_wait_op.hppwin_mutex.hppwin_object_handle_service.hppwin_static_mutex.hppwin_thread.hppwin_tss_ptr.hppwinapp_thread.hppwince_thread.hppwinrt_async_manager.hppwinrt_async_op.hppwinrt_resolve_op.hppwinrt_resolver_service.hppwinrt_socket_connect_op.hppwinrt_socket_recv_op.hppwinrt_socket_send_op.hppwinrt_ssocket_service.hppwinrt_ssocket_service_base.hppwinrt_timer_scheduler.hppwinrt_utils.hppwinsock_init.hppwork_dispatcher.hppwrapped_handler.hpp
dispatch.hpperror.hpperror_code.hppexecution.hpp
execution
execution_context.hppexecutor.hppexecutor_work_guard.hpp
experimental
generic
handler_alloc_hook.hpphandler_continuation_hook.hpphandler_invoke_hook.hpphigh_resolution_timer.hpp
impl
io_context.hppio_context_strand.hppio_service.hppio_service_strand.hpp
ip
is_applicable_property.hppis_executor.hppis_read_buffered.hppis_write_buffered.hpp
local
multiple_exceptions.hpppackaged_task.hppplaceholders.hpp
posix
post.hppprefer.hppquery.hppread.hppread_at.hppread_until.hppredirect_error.hpprequire.hpprequire_concept.hppserial_port.hppserial_port_base.hppsignal_set.hppsocket_base.hppspawn.hppssl.hpp
ssl
static_thread_pool.hppsteady_timer.hppstrand.hppstreambuf.hppsystem_context.hppsystem_error.hppsystem_executor.hppsystem_timer.hppthis_coro.hppthread.hppthread_pool.hpptime_traits.hpp
traits
ts
unyield.hppuse_awaitable.hppuse_future.hppuses_executor.hppversion.hppwait_traits.hpp
windows
write.hppwrite_at.hppyield.hpp
release.pl
src
tsify.pl
CMakeLists.txt
CPR
CURL
CHANGES
CMake
CMakeLists.txtCOPYINGGIT-INFOMacOSX-FrameworkMakefile.amMakefile.distREADMEREADME.mdRELEASE-NOTESSECURITY.mdacinclude.m4appveyor.ymlbuildconfbuildconf.batconfigure.accurl-config.in
include
lib
CMakeLists.txtMakefile.amMakefile.incMakefile.m32Makefile.netwareMakefile.vxworksaltsvc.caltsvc.hamigaos.camigaos.harpa_telnet.hasyn-ares.casyn-thread.casyn.hbase64.cc-hyper.cc-hyper.hchecksrc.plconfig-amigaos.hconfig-dos.hconfig-mac.hconfig-os400.hconfig-plan9.hconfig-riscos.hconfig-tpf.hconfig-vxworks.hconfig-win32.hconfig-win32ce.hconncache.cconncache.hconnect.cconnect.hcontent_encoding.ccontent_encoding.hcookie.ccookie.hcurl_addrinfo.ccurl_addrinfo.hcurl_base64.hcurl_config.h.cmakecurl_ctype.ccurl_ctype.hcurl_des.ccurl_des.hcurl_endian.ccurl_endian.hcurl_fnmatch.ccurl_fnmatch.hcurl_get_line.ccurl_get_line.hcurl_gethostname.ccurl_gethostname.hcurl_gssapi.ccurl_gssapi.hcurl_hmac.hcurl_krb5.hcurl_ldap.hcurl_md4.hcurl_md5.hcurl_memory.hcurl_memrchr.ccurl_memrchr.hcurl_multibyte.ccurl_multibyte.hcurl_ntlm_core.ccurl_ntlm_core.hcurl_ntlm_wb.ccurl_ntlm_wb.hcurl_path.ccurl_path.hcurl_printf.hcurl_range.ccurl_range.hcurl_rtmp.ccurl_rtmp.hcurl_sasl.ccurl_sasl.hcurl_setup.hcurl_setup_once.hcurl_sha256.hcurl_sspi.ccurl_sspi.hcurl_threads.ccurl_threads.hcurlx.hdict.cdict.hdoh.cdoh.hdotdot.cdotdot.hdynbuf.cdynbuf.heasy.ceasygetopt.ceasyif.heasyoptions.ceasyoptions.hescape.cescape.hfile.cfile.hfileinfo.cfileinfo.hfirefox-db2pem.shformdata.cformdata.hftp.cftp.hftplistparser.cftplistparser.hgetenv.cgetinfo.cgetinfo.hgopher.cgopher.hhash.chash.hhmac.chostasyn.chostcheck.chostcheck.hhostip.chostip.hhostip4.chostip6.chostsyn.chsts.chsts.hhttp.chttp.hhttp2.chttp2.hhttp_aws_sigv4.chttp_aws_sigv4.hhttp_chunks.chttp_chunks.hhttp_digest.chttp_digest.hhttp_negotiate.chttp_negotiate.hhttp_ntlm.chttp_ntlm.hhttp_proxy.chttp_proxy.hidn_win32.cif2ip.cif2ip.himap.cimap.hinet_ntop.cinet_ntop.hinet_pton.cinet_pton.hkrb5.cldap.clibcurl.plistlibcurl.rclibcurl.vers.inllist.cllist.hmakefile.amigamakefile.djmd4.cmd5.cmemdebug.cmemdebug.hmime.cmime.hmk-ca-bundle.plmk-ca-bundle.vbsmprintf.cmqtt.cmqtt.hmulti.cmultihandle.hmultiif.hnetrc.cnetrc.hnon-ascii.cnon-ascii.hnonblock.cnonblock.hnwlib.cnwos.copenldap.coptiontable.plparsedate.cparsedate.hpingpong.cpingpong.hpop3.cpop3.hprogress.cprogress.hpsl.cpsl.hquic.hrand.crand.hrename.crename.hrtsp.crtsp.hselect.cselect.hsendf.csendf.hsetopt.csetopt.hsetup-os400.hsetup-vms.hsetup-win32.hsha256.cshare.cshare.hsigpipe.hslist.cslist.hsmb.csmb.hsmtp.csmtp.hsockaddr.hsocketpair.csocketpair.hsocks.csocks.hsocks_gssapi.csocks_sspi.cspeedcheck.cspeedcheck.hsplay.csplay.hstrcase.cstrcase.hstrdup.cstrdup.hstrerror.cstrerror.hstrtok.cstrtok.hstrtoofft.cstrtoofft.hsystem_win32.csystem_win32.htelnet.ctelnet.htftp.ctftp.htimeval.ctimeval.htransfer.ctransfer.hurl.curl.hurlapi-int.hurlapi.curldata.h
vauth
version.cversion_win32.cversion_win32.h
vquic
vssh
vtls
warnless.cwarnless.hwildcard.cwildcard.hx509asn1.cx509asn1.h
libcurl.pc.in
m4
maketgz
packages
plan9
scripts
src
winbuild
Fmt
MDBC
SleepyDiscord
CMakeLists.txtLICENSE.mdREADME.mdazure-pipelines.yml
buildtools
include
sleepy_discord
WebSocketPP
ZLib
CMakeLists.txtChangeLogFAQINDEXMakefileMakefile.inREADMEadler32.c
amiga
compress.cconfigure
contrib
README.contrib
ada
amd64
asm686
blast
delphi
dotzlib
gcc_gvmat64
infback9
inflate86
iostream
iostream2
iostream3
masmx64
masmx86
minizip
pascal
puff
testzlib
untgz
vstudio
crc32.ccrc32.hdeflate.cdeflate.h
doc
examples
gzclose.cgzguts.hgzlib.cgzread.cgzwrite.cinfback.cinffast.cinffast.hinffixed.hinflate.cinflate.hinftrees.cinftrees.hmake_vms.com
msdos
nintendods
old
os400
qnx
test
treebuild.xmltrees.ctrees.huncompr.c
watcom
win32
zconf.h.cmakeinzconf.h.inzconf.h.includedzlib.3zlib.3.pdfzlib.hzlib.mapzlib.pc.cmakeinzlib.pc.inzlib2ansizutil.czutil.h

170
module/Vendor/CURL/lib/vauth/cleartext.c vendored Normal file

@@ -0,0 +1,170 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC4616 PLAIN authentication
* Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
!defined(CURL_DISABLE_POP3)
#include <curl/curl.h>
#include "urldata.h"
#include "vauth/vauth.h"
#include "curl_base64.h"
#include "curl_md5.h"
#include "warnless.h"
#include "strtok.h"
#include "sendf.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_create_plain_message()
*
* This is used to generate an already encoded PLAIN message ready
* for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* authzid [in] - The authorization identity.
* authcid [in] - The authentication identity.
* passwd [in] - The password.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_plain_message(struct Curl_easy *data,
const char *authzid,
const char *authcid,
const char *passwd,
char **outptr, size_t *outlen)
{
CURLcode result;
char *plainauth;
size_t zlen;
size_t clen;
size_t plen;
size_t plainlen;
*outlen = 0;
*outptr = NULL;
zlen = (authzid == NULL ? 0 : strlen(authzid));
clen = strlen(authcid);
plen = strlen(passwd);
/* Compute binary message length. Check for overflows. */
if((zlen > SIZE_T_MAX/4) || (clen > SIZE_T_MAX/4) ||
(plen > (SIZE_T_MAX/2 - 2)))
return CURLE_OUT_OF_MEMORY;
plainlen = zlen + clen + plen + 2;
plainauth = malloc(plainlen);
if(!plainauth)
return CURLE_OUT_OF_MEMORY;
/* Calculate the reply */
if(zlen != 0)
memcpy(plainauth, authzid, zlen);
plainauth[zlen] = '\0';
memcpy(plainauth + zlen + 1, authcid, clen);
plainauth[zlen + clen + 1] = '\0';
memcpy(plainauth + zlen + clen + 2, passwd, plen);
/* Base64 encode the reply */
result = Curl_base64_encode(data, plainauth, plainlen, outptr, outlen);
free(plainauth);
return result;
}
/*
* Curl_auth_create_login_message()
*
* This is used to generate an already encoded LOGIN message containing the
* user name or password ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* valuep [in] - The user name or user's password.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_login_message(struct Curl_easy *data,
const char *valuep, char **outptr,
size_t *outlen)
{
size_t vlen = strlen(valuep);
if(!vlen) {
/* Calculate an empty reply */
*outptr = strdup("=");
if(*outptr) {
*outlen = (size_t) 1;
return CURLE_OK;
}
*outlen = 0;
return CURLE_OUT_OF_MEMORY;
}
/* Base64 encode the value */
return Curl_base64_encode(data, valuep, vlen, outptr, outlen);
}
/*
* Curl_auth_create_external_message()
*
* This is used to generate an already encoded EXTERNAL message containing
* the user name ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* user [in] - The user name.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_external_message(struct Curl_easy *data,
const char *user, char **outptr,
size_t *outlen)
{
/* This is the same formatting as the login message */
return Curl_auth_create_login_message(data, user, outptr, outlen);
}
#endif /* if no users */

138
module/Vendor/CURL/lib/vauth/cram.c vendored Normal file

@@ -0,0 +1,138 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC2195 CRAM-MD5 authentication
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_CRYPTO_AUTH)
#include <curl/curl.h>
#include "urldata.h"
#include "vauth/vauth.h"
#include "curl_base64.h"
#include "curl_hmac.h"
#include "curl_md5.h"
#include "warnless.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_decode_cram_md5_message()
*
* This is used to decode an already encoded CRAM-MD5 challenge message.
*
* Parameters:
*
* chlg64 [in] - The base64 encoded challenge message.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_cram_md5_message(const char *chlg64, char **outptr,
size_t *outlen)
{
CURLcode result = CURLE_OK;
size_t chlg64len = strlen(chlg64);
*outptr = NULL;
*outlen = 0;
/* Decode the challenge if necessary */
if(chlg64len && *chlg64 != '=')
result = Curl_base64_decode(chlg64, (unsigned char **) outptr, outlen);
return result;
}
/*
* Curl_auth_create_cram_md5_message()
*
* This is used to generate an already encoded CRAM-MD5 response message ready
* for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* chlg [in] - The challenge.
* userp [in] - The user name.
* passwdp [in] - The user's password.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_cram_md5_message(struct Curl_easy *data,
const char *chlg,
const char *userp,
const char *passwdp,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
struct HMAC_context *ctxt;
unsigned char digest[MD5_DIGEST_LEN];
char *response;
if(chlg)
chlglen = strlen(chlg);
/* Compute the digest using the password as the key */
ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
(const unsigned char *) passwdp,
curlx_uztoui(strlen(passwdp)));
if(!ctxt)
return CURLE_OUT_OF_MEMORY;
/* Update the digest with the given challenge */
if(chlglen > 0)
Curl_HMAC_update(ctxt, (const unsigned char *) chlg,
curlx_uztoui(chlglen));
/* Finalise the digest */
Curl_HMAC_final(ctxt, digest);
/* Generate the response */
response = aprintf(
"%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
userp, digest[0], digest[1], digest[2], digest[3], digest[4],
digest[5], digest[6], digest[7], digest[8], digest[9], digest[10],
digest[11], digest[12], digest[13], digest[14], digest[15]);
if(!response)
return CURLE_OUT_OF_MEMORY;
/* Base64 encode the response */
result = Curl_base64_encode(data, response, 0, outptr, outlen);
free(response);
return result;
}
#endif /* !CURL_DISABLE_CRYPTO_AUTH */

999
module/Vendor/CURL/lib/vauth/digest.c vendored Normal file

@@ -0,0 +1,999 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC2831 DIGEST-MD5 authentication
* RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_CRYPTO_AUTH)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "vauth/digest.h"
#include "urldata.h"
#include "curl_base64.h"
#include "curl_hmac.h"
#include "curl_md5.h"
#include "curl_sha256.h"
#include "vtls/vtls.h"
#include "warnless.h"
#include "strtok.h"
#include "strcase.h"
#include "non-ascii.h" /* included for Curl_convert_... prototypes */
#include "curl_printf.h"
#include "rand.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
#if !defined(USE_WINDOWS_SSPI)
#define DIGEST_QOP_VALUE_AUTH (1 << 0)
#define DIGEST_QOP_VALUE_AUTH_INT (1 << 1)
#define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2)
#define DIGEST_QOP_VALUE_STRING_AUTH "auth"
#define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int"
#define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
/* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
It converts digest text to ASCII so the MD5 will be correct for
what ultimately goes over the network.
*/
#define CURL_OUTPUT_DIGEST_CONV(a, b) \
do { \
result = Curl_convert_to_network(a, b, strlen(b)); \
if(result) { \
free(b); \
return result; \
} \
} while(0)
#endif /* !USE_WINDOWS_SSPI */
bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
const char **endptr)
{
int c;
bool starts_with_quote = FALSE;
bool escape = FALSE;
for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
*value++ = *str++;
*value = 0;
if('=' != *str++)
/* eek, no match */
return FALSE;
if('\"' == *str) {
/* This starts with a quote so it must end with one as well! */
str++;
starts_with_quote = TRUE;
}
for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
switch(*str) {
case '\\':
if(!escape) {
/* possibly the start of an escaped quote */
escape = TRUE;
*content++ = '\\'; /* Even though this is an escape character, we still
store it as-is in the target buffer */
continue;
}
break;
case ',':
if(!starts_with_quote) {
/* This signals the end of the content if we didn't get a starting
quote and then we do "sloppy" parsing */
c = 0; /* the end */
continue;
}
break;
case '\r':
case '\n':
/* end of string */
c = 0;
continue;
case '\"':
if(!escape && starts_with_quote) {
/* end of string */
c = 0;
continue;
}
break;
}
escape = FALSE;
*content++ = *str;
}
*content = 0;
*endptr = str;
return TRUE;
}
#if !defined(USE_WINDOWS_SSPI)
/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
unsigned char *dest) /* 33 bytes */
{
int i;
for(i = 0; i < 16; i++)
msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
}
/* Convert sha256 chunk to RFC7616 -suitable ascii string*/
static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
unsigned char *dest) /* 65 bytes */
{
int i;
for(i = 0; i < 32; i++)
msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
}
/* Perform quoted-string escaping as described in RFC2616 and its errata */
static char *auth_digest_string_quoted(const char *source)
{
char *dest;
const char *s = source;
size_t n = 1; /* null terminator */
/* Calculate size needed */
while(*s) {
++n;
if(*s == '"' || *s == '\\') {
++n;
}
++s;
}
dest = malloc(n);
if(dest) {
char *d = dest;
s = source;
while(*s) {
if(*s == '"' || *s == '\\') {
*d++ = '\\';
}
*d++ = *s++;
}
*d = 0;
}
return dest;
}
/* Retrieves the value for a corresponding key from the challenge string
* returns TRUE if the key could be found, FALSE if it does not exists
*/
static bool auth_digest_get_key_value(const char *chlg,
const char *key,
char *value,
size_t max_val_len,
char end_char)
{
char *find_pos;
size_t i;
find_pos = strstr(chlg, key);
if(!find_pos)
return FALSE;
find_pos += strlen(key);
for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
value[i] = *find_pos++;
value[i] = '\0';
return TRUE;
}
static CURLcode auth_digest_get_qop_values(const char *options, int *value)
{
char *tmp;
char *token;
char *tok_buf = NULL;
/* Initialise the output */
*value = 0;
/* Tokenise the list of qop values. Use a temporary clone of the buffer since
strtok_r() ruins it. */
tmp = strdup(options);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
token = strtok_r(tmp, ",", &tok_buf);
while(token != NULL) {
if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
*value |= DIGEST_QOP_VALUE_AUTH;
else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
*value |= DIGEST_QOP_VALUE_AUTH_INT;
else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
*value |= DIGEST_QOP_VALUE_AUTH_CONF;
token = strtok_r(NULL, ",", &tok_buf);
}
free(tmp);
return CURLE_OK;
}
/*
* auth_decode_digest_md5_message()
*
* This is used internally to decode an already encoded DIGEST-MD5 challenge
* message into the separate attributes.
*
* Parameters:
*
* chlg64 [in] - The base64 encoded challenge message.
* nonce [in/out] - The buffer where the nonce will be stored.
* nlen [in] - The length of the nonce buffer.
* realm [in/out] - The buffer where the realm will be stored.
* rlen [in] - The length of the realm buffer.
* alg [in/out] - The buffer where the algorithm will be stored.
* alen [in] - The length of the algorithm buffer.
* qop [in/out] - The buffer where the qop-options will be stored.
* qlen [in] - The length of the qop buffer.
*
* Returns CURLE_OK on success.
*/
static CURLcode auth_decode_digest_md5_message(const char *chlg64,
char *nonce, size_t nlen,
char *realm, size_t rlen,
char *alg, size_t alen,
char *qop, size_t qlen)
{
CURLcode result = CURLE_OK;
unsigned char *chlg = NULL;
size_t chlglen = 0;
size_t chlg64len = strlen(chlg64);
/* Decode the base-64 encoded challenge message */
if(chlg64len && *chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg)
return CURLE_BAD_CONTENT_ENCODING;
/* Retrieve nonce string from the challenge */
if(!auth_digest_get_key_value((char *) chlg, "nonce=\"", nonce, nlen,
'\"')) {
free(chlg);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Retrieve realm string from the challenge */
if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen,
'\"')) {
/* Challenge does not have a realm, set empty string [RFC2831] page 6 */
strcpy(realm, "");
}
/* Retrieve algorithm string from the challenge */
if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) {
free(chlg);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Retrieve qop-options string from the challenge */
if(!auth_digest_get_key_value((char *) chlg, "qop=\"", qop, qlen, '\"')) {
free(chlg);
return CURLE_BAD_CONTENT_ENCODING;
}
free(chlg);
return CURLE_OK;
}
/*
* Curl_auth_is_digest_supported()
*
* This is used to evaluate if DIGEST is supported.
*
* Parameters: None
*
* Returns TRUE as DIGEST as handled by libcurl.
*/
bool Curl_auth_is_digest_supported(void)
{
return TRUE;
}
/*
* Curl_auth_create_digest_md5_message()
*
* This is used to generate an already encoded DIGEST-MD5 response message
* ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* chlg64 [in] - The base64 encoded challenge message.
* userp [in] - The user name.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
const char *chlg64,
const char *userp,
const char *passwdp,
const char *service,
char **outptr, size_t *outlen)
{
size_t i;
struct MD5_context *ctxt;
char *response = NULL;
unsigned char digest[MD5_DIGEST_LEN];
char HA1_hex[2 * MD5_DIGEST_LEN + 1];
char HA2_hex[2 * MD5_DIGEST_LEN + 1];
char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
char nonce[64];
char realm[128];
char algorithm[64];
char qop_options[64];
int qop_values;
char cnonce[33];
char nonceCount[] = "00000001";
char method[] = "AUTHENTICATE";
char qop[] = DIGEST_QOP_VALUE_STRING_AUTH;
char *spn = NULL;
/* Decode the challenge message */
CURLcode result = auth_decode_digest_md5_message(chlg64, nonce,
sizeof(nonce), realm,
sizeof(realm), algorithm,
sizeof(algorithm),
qop_options,
sizeof(qop_options));
if(result)
return result;
/* We only support md5 sessions */
if(strcmp(algorithm, "md5-sess") != 0)
return CURLE_BAD_CONTENT_ENCODING;
/* Get the qop-values from the qop-options */
result = auth_digest_get_qop_values(qop_options, &qop_values);
if(result)
return result;
/* We only support auth quality-of-protection */
if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
return CURLE_BAD_CONTENT_ENCODING;
/* Generate 32 random hex chars, 32 bytes + 1 zero termination */
result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
if(result)
return result;
/* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt)
return CURLE_OUT_OF_MEMORY;
Curl_MD5_update(ctxt, (const unsigned char *) userp,
curlx_uztoui(strlen(userp)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) realm,
curlx_uztoui(strlen(realm)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
curlx_uztoui(strlen(passwdp)));
Curl_MD5_final(ctxt, digest);
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt)
return CURLE_OUT_OF_MEMORY;
Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) nonce,
curlx_uztoui(strlen(nonce)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
curlx_uztoui(strlen(cnonce)));
Curl_MD5_final(ctxt, digest);
/* Convert calculated 16 octet hex into 32 bytes string */
for(i = 0; i < MD5_DIGEST_LEN; i++)
msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
/* Generate our SPN */
spn = Curl_auth_build_spn(service, realm, NULL);
if(!spn)
return CURLE_OUT_OF_MEMORY;
/* Calculate H(A2) */
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt) {
free(spn);
return CURLE_OUT_OF_MEMORY;
}
Curl_MD5_update(ctxt, (const unsigned char *) method,
curlx_uztoui(strlen(method)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) spn,
curlx_uztoui(strlen(spn)));
Curl_MD5_final(ctxt, digest);
for(i = 0; i < MD5_DIGEST_LEN; i++)
msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
/* Now calculate the response hash */
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt) {
free(spn);
return CURLE_OUT_OF_MEMORY;
}
Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) nonce,
curlx_uztoui(strlen(nonce)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
curlx_uztoui(strlen(nonceCount)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
curlx_uztoui(strlen(cnonce)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) qop,
curlx_uztoui(strlen(qop)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
Curl_MD5_final(ctxt, digest);
for(i = 0; i < MD5_DIGEST_LEN; i++)
msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
/* Generate the response */
response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
"cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
"qop=%s",
userp, realm, nonce,
cnonce, nonceCount, spn, resp_hash_hex, qop);
free(spn);
if(!response)
return CURLE_OUT_OF_MEMORY;
/* Base64 encode the response */
result = Curl_base64_encode(data, response, 0, outptr, outlen);
free(response);
return result;
}
/*
* Curl_auth_decode_digest_http_message()
*
* This is used to decode a HTTP DIGEST challenge message into the separate
* attributes.
*
* Parameters:
*
* chlg [in] - The challenge message.
* digest [in/out] - The digest data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
struct digestdata *digest)
{
bool before = FALSE; /* got a nonce before */
bool foundAuth = FALSE;
bool foundAuthInt = FALSE;
char *token = NULL;
char *tmp = NULL;
/* If we already have received a nonce, keep that in mind */
if(digest->nonce)
before = TRUE;
/* Clean up any former leftovers and initialise to defaults */
Curl_auth_digest_cleanup(digest);
for(;;) {
char value[DIGEST_MAX_VALUE_LENGTH];
char content[DIGEST_MAX_CONTENT_LENGTH];
/* Pass all additional spaces here */
while(*chlg && ISSPACE(*chlg))
chlg++;
/* Extract a value=content pair */
if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
if(strcasecompare(value, "nonce")) {
free(digest->nonce);
digest->nonce = strdup(content);
if(!digest->nonce)
return CURLE_OUT_OF_MEMORY;
}
else if(strcasecompare(value, "stale")) {
if(strcasecompare(content, "true")) {
digest->stale = TRUE;
digest->nc = 1; /* we make a new nonce now */
}
}
else if(strcasecompare(value, "realm")) {
free(digest->realm);
digest->realm = strdup(content);
if(!digest->realm)
return CURLE_OUT_OF_MEMORY;
}
else if(strcasecompare(value, "opaque")) {
free(digest->opaque);
digest->opaque = strdup(content);
if(!digest->opaque)
return CURLE_OUT_OF_MEMORY;
}
else if(strcasecompare(value, "qop")) {
char *tok_buf = NULL;
/* Tokenize the list and choose auth if possible, use a temporary
clone of the buffer since strtok_r() ruins it */
tmp = strdup(content);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
token = strtok_r(tmp, ",", &tok_buf);
while(token != NULL) {
if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
foundAuth = TRUE;
}
else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
foundAuthInt = TRUE;
}
token = strtok_r(NULL, ",", &tok_buf);
}
free(tmp);
/* Select only auth or auth-int. Otherwise, ignore */
if(foundAuth) {
free(digest->qop);
digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
if(!digest->qop)
return CURLE_OUT_OF_MEMORY;
}
else if(foundAuthInt) {
free(digest->qop);
digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
if(!digest->qop)
return CURLE_OUT_OF_MEMORY;
}
}
else if(strcasecompare(value, "algorithm")) {
free(digest->algorithm);
digest->algorithm = strdup(content);
if(!digest->algorithm)
return CURLE_OUT_OF_MEMORY;
if(strcasecompare(content, "MD5-sess"))
digest->algo = CURLDIGESTALGO_MD5SESS;
else if(strcasecompare(content, "MD5"))
digest->algo = CURLDIGESTALGO_MD5;
else if(strcasecompare(content, "SHA-256"))
digest->algo = CURLDIGESTALGO_SHA256;
else if(strcasecompare(content, "SHA-256-SESS"))
digest->algo = CURLDIGESTALGO_SHA256SESS;
else if(strcasecompare(content, "SHA-512-256"))
digest->algo = CURLDIGESTALGO_SHA512_256;
else if(strcasecompare(content, "SHA-512-256-SESS"))
digest->algo = CURLDIGESTALGO_SHA512_256SESS;
else
return CURLE_BAD_CONTENT_ENCODING;
}
else if(strcasecompare(value, "userhash")) {
if(strcasecompare(content, "true")) {
digest->userhash = TRUE;
}
}
else {
/* Unknown specifier, ignore it! */
}
}
else
break; /* We're done here */
/* Pass all additional spaces here */
while(*chlg && ISSPACE(*chlg))
chlg++;
/* Allow the list to be comma-separated */
if(',' == *chlg)
chlg++;
}
/* We had a nonce since before, and we got another one now without
'stale=true'. This means we provided bad credentials in the previous
request */
if(before && !digest->stale)
return CURLE_BAD_CONTENT_ENCODING;
/* We got this header without a nonce, that's a bad Digest line! */
if(!digest->nonce)
return CURLE_BAD_CONTENT_ENCODING;
return CURLE_OK;
}
/*
* auth_create_digest_http_message()
*
* This is used to generate a HTTP DIGEST response message ready for sending
* to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name.
* passwdp [in] - The user's password.
* request [in] - The HTTP request.
* uripath [in] - The path of the HTTP uri.
* digest [in/out] - The digest data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
static CURLcode auth_create_digest_http_message(
struct Curl_easy *data,
const char *userp,
const char *passwdp,
const unsigned char *request,
const unsigned char *uripath,
struct digestdata *digest,
char **outptr, size_t *outlen,
void (*convert_to_ascii)(unsigned char *, unsigned char *),
void (*hash)(unsigned char *, const unsigned char *,
const size_t))
{
CURLcode result;
unsigned char hashbuf[32]; /* 32 bytes/256 bits */
unsigned char request_digest[65];
unsigned char ha1[65]; /* 64 digits and 1 zero byte */
unsigned char ha2[65]; /* 64 digits and 1 zero byte */
char userh[65];
char *cnonce = NULL;
size_t cnonce_sz = 0;
char *userp_quoted;
char *response = NULL;
char *hashthis = NULL;
char *tmp = NULL;
if(!digest->nc)
digest->nc = 1;
if(!digest->cnonce) {
char cnoncebuf[33];
result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
sizeof(cnoncebuf));
if(result)
return result;
result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
&cnonce, &cnonce_sz);
if(result)
return result;
digest->cnonce = cnonce;
}
if(digest->userhash) {
hashthis = aprintf("%s:%s", userp, digest->realm);
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, hashthis);
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
free(hashthis);
convert_to_ascii(hashbuf, (unsigned char *)userh);
}
/*
If the algorithm is "MD5" or unspecified (which then defaults to MD5):
A1 = unq(username-value) ":" unq(realm-value) ":" passwd
If the algorithm is "MD5-sess" then:
A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
unq(nonce-value) ":" unq(cnonce-value)
*/
hashthis = aprintf("%s:%s:%s", digest->userhash ? userh : userp,
digest->realm, passwdp);
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
free(hashthis);
convert_to_ascii(hashbuf, ha1);
if(digest->algo == CURLDIGESTALGO_MD5SESS ||
digest->algo == CURLDIGESTALGO_SHA256SESS ||
digest->algo == CURLDIGESTALGO_SHA512_256SESS) {
/* nonce and cnonce are OUTSIDE the hash */
tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
free(tmp);
convert_to_ascii(hashbuf, ha1);
}
/*
If the "qop" directive's value is "auth" or is unspecified, then A2 is:
A2 = Method ":" digest-uri-value
If the "qop" value is "auth-int", then A2 is:
A2 = Method ":" digest-uri-value ":" H(entity-body)
(The "Method" value is the HTTP request method as specified in section
5.1.1 of RFC 2616)
*/
hashthis = aprintf("%s:%s", request, uripath);
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
/* We don't support auth-int for PUT or POST */
char hashed[65];
char *hashthis2;
hash(hashbuf, (const unsigned char *)"", 0);
convert_to_ascii(hashbuf, (unsigned char *)hashed);
hashthis2 = aprintf("%s:%s", hashthis, hashed);
free(hashthis);
hashthis = hashthis2;
}
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
free(hashthis);
convert_to_ascii(hashbuf, ha2);
if(digest->qop) {
hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc,
digest->cnonce, digest->qop, ha2);
}
else {
hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2);
}
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
free(hashthis);
convert_to_ascii(hashbuf, request_digest);
/* For test case 64 (snooped from a Mozilla 1.3a request)
Authorization: Digest username="testuser", realm="testrealm", \
nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
Digest parameters are all quoted strings. Username which is provided by
the user will need double quotes and backslashes within it escaped. For
the other fields, this shouldn't be an issue. realm, nonce, and opaque
are copied as is from the server, escapes and all. cnonce is generated
with web-safe characters. uri is already percent encoded. nc is 8 hex
characters. algorithm and qop with standard values only contain web-safe
characters.
*/
userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
if(!userp_quoted)
return CURLE_OUT_OF_MEMORY;
if(digest->qop) {
response = aprintf("username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"cnonce=\"%s\", "
"nc=%08x, "
"qop=%s, "
"response=\"%s\"",
userp_quoted,
digest->realm,
digest->nonce,
uripath,
digest->cnonce,
digest->nc,
digest->qop,
request_digest);
if(strcasecompare(digest->qop, "auth"))
digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
padded which tells to the server how many times you are
using the same nonce in the qop=auth mode */
}
else {
response = aprintf("username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"response=\"%s\"",
userp_quoted,
digest->realm,
digest->nonce,
uripath,
request_digest);
}
free(userp_quoted);
if(!response)
return CURLE_OUT_OF_MEMORY;
/* Add the optional fields */
if(digest->opaque) {
/* Append the opaque */
tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
free(response);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
response = tmp;
}
if(digest->algorithm) {
/* Append the algorithm */
tmp = aprintf("%s, algorithm=%s", response, digest->algorithm);
free(response);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
response = tmp;
}
if(digest->userhash) {
/* Append the userhash */
tmp = aprintf("%s, userhash=true", response);
free(response);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
response = tmp;
}
/* Return the output */
*outptr = response;
*outlen = strlen(response);
return CURLE_OK;
}
/*
* Curl_auth_create_digest_http_message()
*
* This is used to generate a HTTP DIGEST response message ready for sending
* to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name.
* passwdp [in] - The user's password.
* request [in] - The HTTP request.
* uripath [in] - The path of the HTTP uri.
* digest [in/out] - The digest data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const unsigned char *request,
const unsigned char *uripath,
struct digestdata *digest,
char **outptr, size_t *outlen)
{
switch(digest->algo) {
case CURLDIGESTALGO_MD5:
case CURLDIGESTALGO_MD5SESS:
return auth_create_digest_http_message(data, userp, passwdp,
request, uripath, digest,
outptr, outlen,
auth_digest_md5_to_ascii,
Curl_md5it);
case CURLDIGESTALGO_SHA256:
case CURLDIGESTALGO_SHA256SESS:
case CURLDIGESTALGO_SHA512_256:
case CURLDIGESTALGO_SHA512_256SESS:
return auth_create_digest_http_message(data, userp, passwdp,
request, uripath, digest,
outptr, outlen,
auth_digest_sha256_to_ascii,
Curl_sha256it);
default:
return CURLE_UNSUPPORTED_PROTOCOL;
}
}
/*
* Curl_auth_digest_cleanup()
*
* This is used to clean up the digest specific data.
*
* Parameters:
*
* digest [in/out] - The digest data struct being cleaned up.
*
*/
void Curl_auth_digest_cleanup(struct digestdata *digest)
{
Curl_safefree(digest->nonce);
Curl_safefree(digest->cnonce);
Curl_safefree(digest->realm);
Curl_safefree(digest->opaque);
Curl_safefree(digest->qop);
Curl_safefree(digest->algorithm);
digest->nc = 0;
digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
digest->stale = FALSE; /* default means normal, not stale */
digest->userhash = FALSE;
}
#endif /* !USE_WINDOWS_SSPI */
#endif /* CURL_DISABLE_CRYPTO_AUTH */

47
module/Vendor/CURL/lib/vauth/digest.h vendored Normal file

@@ -0,0 +1,47 @@
#ifndef HEADER_CURL_DIGEST_H
#define HEADER_CURL_DIGEST_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include <curl/curl.h>
#if !defined(CURL_DISABLE_CRYPTO_AUTH)
#define DIGEST_MAX_VALUE_LENGTH 256
#define DIGEST_MAX_CONTENT_LENGTH 1024
enum {
CURLDIGESTALGO_MD5,
CURLDIGESTALGO_MD5SESS,
CURLDIGESTALGO_SHA256,
CURLDIGESTALGO_SHA256SESS,
CURLDIGESTALGO_SHA512_256,
CURLDIGESTALGO_SHA512_256SESS
};
/* This is used to extract the realm from a challenge message */
bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
const char **endptr);
#endif
#endif /* HEADER_CURL_DIGEST_H */

@@ -0,0 +1,694 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
* Copyright (C) 2015 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC2831 DIGEST-MD5 authentication
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "vauth/digest.h"
#include "urldata.h"
#include "curl_base64.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
#include "strdup.h"
#include "strcase.h"
#include "strerror.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_digest_supported()
*
* This is used to evaluate if DIGEST is supported.
*
* Parameters: None
*
* Returns TRUE if DIGEST is supported by Windows SSPI.
*/
bool Curl_auth_is_digest_supported(void)
{
PSecPkgInfo SecurityPackage;
SECURITY_STATUS status;
/* Query the security package for Digest */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
&SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
s_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
}
/*
* Curl_auth_create_digest_md5_message()
*
* This is used to generate an already encoded DIGEST-MD5 response message
* ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* chlg64 [in] - The base64 encoded challenge message.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
const char *chlg64,
const char *userp,
const char *passwdp,
const char *service,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
TCHAR *spn = NULL;
size_t chlglen = 0;
size_t token_max = 0;
unsigned char *input_token = NULL;
unsigned char *output_token = NULL;
CredHandle credentials;
CtxtHandle context;
PSecPkgInfo SecurityPackage;
SEC_WINNT_AUTH_IDENTITY identity;
SEC_WINNT_AUTH_IDENTITY *p_identity;
SecBuffer chlg_buf;
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
SecBufferDesc resp_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
/* Decode the base-64 encoded challenge message */
if(strlen(chlg64) && *chlg64 != '=') {
result = Curl_base64_decode(chlg64, &input_token, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!input_token) {
infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Query the security package for DigestSSP */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
&SecurityPackage);
if(status != SEC_E_OK) {
free(input_token);
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our response buffer */
output_token = malloc(token_max);
if(!output_token) {
free(input_token);
return CURLE_OUT_OF_MEMORY;
}
/* Generate our SPN */
spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
if(!spn) {
free(output_token);
free(input_token);
return CURLE_OUT_OF_MEMORY;
}
if(userp && *userp) {
/* Populate our identity structure */
result = Curl_create_sspi_identity(userp, passwdp, &identity);
if(result) {
free(spn);
free(output_token);
free(input_token);
return result;
}
/* Allow proper cleanup of the identity structure */
p_identity = &identity;
}
else
/* Use the current Windows user */
p_identity = NULL;
/* Acquire our credentials handle */
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_DIGEST),
SECPKG_CRED_OUTBOUND, NULL,
p_identity, NULL, NULL,
&credentials, &expiry);
if(status != SEC_E_OK) {
Curl_sspi_free_identity(p_identity);
free(spn);
free(output_token);
free(input_token);
return CURLE_LOGIN_DENIED;
}
/* Setup the challenge "input" security buffer */
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 1;
chlg_desc.pBuffers = &chlg_buf;
chlg_buf.BufferType = SECBUFFER_TOKEN;
chlg_buf.pvBuffer = input_token;
chlg_buf.cbBuffer = curlx_uztoul(chlglen);
/* Setup the response "output" security buffer */
resp_desc.ulVersion = SECBUFFER_VERSION;
resp_desc.cBuffers = 1;
resp_desc.pBuffers = &resp_buf;
resp_buf.BufferType = SECBUFFER_TOKEN;
resp_buf.pvBuffer = output_token;
resp_buf.cbBuffer = curlx_uztoul(token_max);
/* Generate our response message */
status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
0, 0, 0, &chlg_desc, 0,
&context, &resp_desc, &attrs,
&expiry);
if(status == SEC_I_COMPLETE_NEEDED ||
status == SEC_I_COMPLETE_AND_CONTINUE)
s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
char buffer[STRERROR_LEN];
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(spn);
free(output_token);
free(input_token);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
infof(data, "schannel: InitializeSecurityContext failed: %s\n",
Curl_sspi_strerror(status, buffer, sizeof(buffer)));
return CURLE_AUTH_ERROR;
}
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
outptr, outlen);
/* Free our handles */
s_pSecFn->DeleteSecurityContext(&context);
s_pSecFn->FreeCredentialsHandle(&credentials);
/* Free the identity structure */
Curl_sspi_free_identity(p_identity);
/* Free the SPN */
free(spn);
/* Free the response buffer */
free(output_token);
/* Free the decoded challenge message */
free(input_token);
return result;
}
/*
* Curl_override_sspi_http_realm()
*
* This is used to populate the domain in a SSPI identity structure
* The realm is extracted from the challenge message and used as the
* domain if it is not already explicitly set.
*
* Parameters:
*
* chlg [in] - The challenge message.
* identity [in/out] - The identity structure.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_override_sspi_http_realm(const char *chlg,
SEC_WINNT_AUTH_IDENTITY *identity)
{
xcharp_u domain, dup_domain;
/* If domain is blank or unset, check challenge message for realm */
if(!identity->Domain || !identity->DomainLength) {
for(;;) {
char value[DIGEST_MAX_VALUE_LENGTH];
char content[DIGEST_MAX_CONTENT_LENGTH];
/* Pass all additional spaces here */
while(*chlg && ISSPACE(*chlg))
chlg++;
/* Extract a value=content pair */
if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
if(strcasecompare(value, "realm")) {
/* Setup identity's domain and length */
domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content);
if(!domain.tchar_ptr)
return CURLE_OUT_OF_MEMORY;
dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
if(!dup_domain.tchar_ptr) {
curlx_unicodefree(domain.tchar_ptr);
return CURLE_OUT_OF_MEMORY;
}
free(identity->Domain);
identity->Domain = dup_domain.tbyte_ptr;
identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
dup_domain.tchar_ptr = NULL;
curlx_unicodefree(domain.tchar_ptr);
}
else {
/* Unknown specifier, ignore it! */
}
}
else
break; /* We're done here */
/* Pass all additional spaces here */
while(*chlg && ISSPACE(*chlg))
chlg++;
/* Allow the list to be comma-separated */
if(',' == *chlg)
chlg++;
}
}
return CURLE_OK;
}
/*
* Curl_auth_decode_digest_http_message()
*
* This is used to decode a HTTP DIGEST challenge message into the separate
* attributes.
*
* Parameters:
*
* chlg [in] - The challenge message.
* digest [in/out] - The digest data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
struct digestdata *digest)
{
size_t chlglen = strlen(chlg);
/* We had an input token before so if there's another one now that means we
provided bad credentials in the previous request or it's stale. */
if(digest->input_token) {
bool stale = false;
const char *p = chlg;
/* Check for the 'stale' directive */
for(;;) {
char value[DIGEST_MAX_VALUE_LENGTH];
char content[DIGEST_MAX_CONTENT_LENGTH];
while(*p && ISSPACE(*p))
p++;
if(!Curl_auth_digest_get_pair(p, value, content, &p))
break;
if(strcasecompare(value, "stale") &&
strcasecompare(content, "true")) {
stale = true;
break;
}
while(*p && ISSPACE(*p))
p++;
if(',' == *p)
p++;
}
if(stale)
Curl_auth_digest_cleanup(digest);
else
return CURLE_LOGIN_DENIED;
}
/* Store the challenge for use later */
digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen + 1);
if(!digest->input_token)
return CURLE_OUT_OF_MEMORY;
digest->input_token_len = chlglen;
return CURLE_OK;
}
/*
* Curl_auth_create_digest_http_message()
*
* This is used to generate a HTTP DIGEST response message ready for sending
* to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* request [in] - The HTTP request.
* uripath [in] - The path of the HTTP uri.
* digest [in/out] - The digest data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const unsigned char *request,
const unsigned char *uripath,
struct digestdata *digest,
char **outptr, size_t *outlen)
{
size_t token_max;
char *resp;
BYTE *output_token;
size_t output_token_len = 0;
PSecPkgInfo SecurityPackage;
SecBuffer chlg_buf[5];
SecBufferDesc chlg_desc;
SECURITY_STATUS status;
(void) data;
/* Query the security package for DigestSSP */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
&SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate the output buffer according to the max token size as indicated
by the security package */
output_token = malloc(token_max);
if(!output_token) {
return CURLE_OUT_OF_MEMORY;
}
/* If the user/passwd that was used to make the identity for http_context
has changed then delete that context. */
if((userp && !digest->user) || (!userp && digest->user) ||
(passwdp && !digest->passwd) || (!passwdp && digest->passwd) ||
(userp && digest->user && strcmp(userp, digest->user)) ||
(passwdp && digest->passwd && strcmp(passwdp, digest->passwd))) {
if(digest->http_context) {
s_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
Curl_safefree(digest->user);
Curl_safefree(digest->passwd);
}
if(digest->http_context) {
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 5;
chlg_desc.pBuffers = chlg_buf;
chlg_buf[0].BufferType = SECBUFFER_TOKEN;
chlg_buf[0].pvBuffer = NULL;
chlg_buf[0].cbBuffer = 0;
chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[1].pvBuffer = (void *) request;
chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[2].pvBuffer = (void *) uripath;
chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath));
chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[3].pvBuffer = NULL;
chlg_buf[3].cbBuffer = 0;
chlg_buf[4].BufferType = SECBUFFER_PADDING;
chlg_buf[4].pvBuffer = output_token;
chlg_buf[4].cbBuffer = curlx_uztoul(token_max);
status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0);
if(status == SEC_E_OK)
output_token_len = chlg_buf[4].cbBuffer;
else { /* delete the context so a new one can be made */
infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx\n",
(long)status);
s_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
}
if(!digest->http_context) {
CredHandle credentials;
SEC_WINNT_AUTH_IDENTITY identity;
SEC_WINNT_AUTH_IDENTITY *p_identity;
SecBuffer resp_buf;
SecBufferDesc resp_desc;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
TCHAR *spn;
/* free the copy of user/passwd used to make the previous identity */
Curl_safefree(digest->user);
Curl_safefree(digest->passwd);
if(userp && *userp) {
/* Populate our identity structure */
if(Curl_create_sspi_identity(userp, passwdp, &identity)) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Populate our identity domain */
if(Curl_override_sspi_http_realm((const char *) digest->input_token,
&identity)) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Allow proper cleanup of the identity structure */
p_identity = &identity;
}
else
/* Use the current Windows user */
p_identity = NULL;
if(userp) {
digest->user = strdup(userp);
if(!digest->user) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
}
if(passwdp) {
digest->passwd = strdup(passwdp);
if(!digest->passwd) {
free(output_token);
Curl_safefree(digest->user);
return CURLE_OUT_OF_MEMORY;
}
}
/* Acquire our credentials handle */
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_DIGEST),
SECPKG_CRED_OUTBOUND, NULL,
p_identity, NULL, NULL,
&credentials, &expiry);
if(status != SEC_E_OK) {
Curl_sspi_free_identity(p_identity);
free(output_token);
return CURLE_LOGIN_DENIED;
}
/* Setup the challenge "input" security buffer if present */
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 3;
chlg_desc.pBuffers = chlg_buf;
chlg_buf[0].BufferType = SECBUFFER_TOKEN;
chlg_buf[0].pvBuffer = digest->input_token;
chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[1].pvBuffer = (void *) request;
chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[2].pvBuffer = NULL;
chlg_buf[2].cbBuffer = 0;
/* Setup the response "output" security buffer */
resp_desc.ulVersion = SECBUFFER_VERSION;
resp_desc.cBuffers = 1;
resp_desc.pBuffers = &resp_buf;
resp_buf.BufferType = SECBUFFER_TOKEN;
resp_buf.pvBuffer = output_token;
resp_buf.cbBuffer = curlx_uztoul(token_max);
spn = curlx_convert_UTF8_to_tchar((char *) uripath);
if(!spn) {
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Allocate our new context handle */
digest->http_context = calloc(1, sizeof(CtxtHandle));
if(!digest->http_context)
return CURLE_OUT_OF_MEMORY;
/* Generate our response message */
status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
spn,
ISC_REQ_USE_HTTP_STYLE, 0, 0,
&chlg_desc, 0,
digest->http_context,
&resp_desc, &attrs, &expiry);
curlx_unicodefree(spn);
if(status == SEC_I_COMPLETE_NEEDED ||
status == SEC_I_COMPLETE_AND_CONTINUE)
s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
char buffer[STRERROR_LEN];
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(output_token);
Curl_safefree(digest->http_context);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
infof(data, "schannel: InitializeSecurityContext failed: %s\n",
Curl_sspi_strerror(status, buffer, sizeof(buffer)));
return CURLE_AUTH_ERROR;
}
output_token_len = resp_buf.cbBuffer;
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
}
resp = malloc(output_token_len + 1);
if(!resp) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Copy the generated response */
memcpy(resp, output_token, output_token_len);
resp[output_token_len] = 0;
/* Return the response */
*outptr = resp;
*outlen = output_token_len;
/* Free the response buffer */
free(output_token);
return CURLE_OK;
}
/*
* Curl_auth_digest_cleanup()
*
* This is used to clean up the digest specific data.
*
* Parameters:
*
* digest [in/out] - The digest data struct being cleaned up.
*
*/
void Curl_auth_digest_cleanup(struct digestdata *digest)
{
/* Free the input token */
Curl_safefree(digest->input_token);
/* Reset any variables */
digest->input_token_len = 0;
/* Delete security context */
if(digest->http_context) {
s_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
/* Free the copy of user/passwd used to make the identity for http_context */
Curl_safefree(digest->user);
Curl_safefree(digest->passwd);
}
#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */

@@ -0,0 +1,401 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
* Copyright (C) 2015 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "curl_sasl.h"
#include "urldata.h"
#include "curl_base64.h"
#include "curl_gssapi.h"
#include "sendf.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_gssapi_supported()
*
* This is used to evaluate if GSSAPI (Kerberos V5) is supported.
*
* Parameters: None
*
* Returns TRUE if Kerberos V5 is supported by the GSS-API library.
*/
bool Curl_auth_is_gssapi_supported(void)
{
return TRUE;
}
/*
* Curl_auth_create_gssapi_user_message()
*
* This is used to generate an already encoded GSSAPI (Kerberos V5) user token
* message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in[ - The host name.
* mutual_auth [in] - Flag specifying whether or not mutual authentication
* is enabled.
* chlg64 [in] - Pointer to the optional base64 encoded challenge
* message.
* krb5 [in/out] - The Kerberos 5 data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
const bool mutual_auth,
const char *chlg64,
struct kerberos5data *krb5,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
unsigned char *chlg = NULL;
OM_uint32 major_status;
OM_uint32 minor_status;
OM_uint32 unused_status;
gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
(void) userp;
(void) passwdp;
if(!krb5->spn) {
/* Generate our SPN */
char *spn = Curl_auth_build_spn(service, NULL, host);
if(!spn)
return CURLE_OUT_OF_MEMORY;
/* Populate the SPN structure */
spn_token.value = spn;
spn_token.length = strlen(spn);
/* Import the SPN */
major_status = gss_import_name(&minor_status, &spn_token,
GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_import_name() failed: ",
major_status, minor_status);
free(spn);
return CURLE_AUTH_ERROR;
}
free(spn);
}
if(chlg64 && *chlg64) {
/* Decode the base-64 encoded challenge message */
if(*chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "GSSAPI handshake failure (empty challenge message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Setup the challenge "input" security buffer */
input_token.value = chlg;
input_token.length = chlglen;
}
major_status = Curl_gss_init_sec_context(data,
&minor_status,
&krb5->context,
krb5->spn,
&Curl_krb5_mech_oid,
GSS_C_NO_CHANNEL_BINDINGS,
&input_token,
&output_token,
mutual_auth,
NULL);
/* Free the decoded challenge as it is not required anymore */
free(input_token.value);
if(GSS_ERROR(major_status)) {
if(output_token.value)
gss_release_buffer(&unused_status, &output_token);
Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
major_status, minor_status);
return CURLE_AUTH_ERROR;
}
if(output_token.value && output_token.length) {
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *) output_token.value,
output_token.length, outptr, outlen);
gss_release_buffer(&unused_status, &output_token);
}
else if(mutual_auth) {
*outptr = strdup("");
if(!*outptr)
result = CURLE_OUT_OF_MEMORY;
}
return result;
}
/*
* Curl_auth_create_gssapi_security_message()
*
* This is used to generate an already encoded GSSAPI (Kerberos V5) security
* token message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* chlg64 [in] - Pointer to the optional base64 encoded challenge message.
* krb5 [in/out] - The Kerberos 5 data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
const char *chlg64,
struct kerberos5data *krb5,
char **outptr,
size_t *outlen)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
size_t messagelen = 0;
unsigned char *chlg = NULL;
unsigned char *message = NULL;
OM_uint32 major_status;
OM_uint32 minor_status;
OM_uint32 unused_status;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
unsigned int indata = 0;
unsigned int outdata = 0;
gss_qop_t qop = GSS_C_QOP_DEFAULT;
unsigned int sec_layer = 0;
unsigned int max_size = 0;
gss_name_t username = GSS_C_NO_NAME;
gss_buffer_desc username_token;
/* Decode the base-64 encoded input message */
if(strlen(chlg64) && *chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "GSSAPI handshake failure (empty security message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Get the fully qualified username back from the context */
major_status = gss_inquire_context(&minor_status, krb5->context,
&username, NULL, NULL, NULL, NULL,
NULL, NULL);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_inquire_context() failed: ",
major_status, minor_status);
free(chlg);
return CURLE_AUTH_ERROR;
}
/* Convert the username from internal format to a displayable token */
major_status = gss_display_name(&minor_status, username,
&username_token, NULL);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_display_name() failed: ",
major_status, minor_status);
free(chlg);
return CURLE_AUTH_ERROR;
}
/* Setup the challenge "input" security buffer */
input_token.value = chlg;
input_token.length = chlglen;
/* Decrypt the inbound challenge and obtain the qop */
major_status = gss_unwrap(&minor_status, krb5->context, &input_token,
&output_token, NULL, &qop);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_unwrap() failed: ",
major_status, minor_status);
gss_release_buffer(&unused_status, &username_token);
free(chlg);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Not 4 octets long so fail as per RFC4752 Section 3.1 */
if(output_token.length != 4) {
infof(data, "GSSAPI handshake failure (invalid security data)\n");
gss_release_buffer(&unused_status, &username_token);
free(chlg);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Copy the data out and free the challenge as it is not required anymore */
memcpy(&indata, output_token.value, 4);
gss_release_buffer(&unused_status, &output_token);
free(chlg);
/* Extract the security layer */
sec_layer = indata & 0x000000FF;
if(!(sec_layer & GSSAUTH_P_NONE)) {
infof(data, "GSSAPI handshake failure (invalid security layer)\n");
gss_release_buffer(&unused_status, &username_token);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Extract the maximum message size the server can receive */
max_size = ntohl(indata & 0xFFFFFF00);
if(max_size > 0) {
/* The server has told us it supports a maximum receive buffer, however, as
we don't require one unless we are encrypting data, we tell the server
our receive buffer is zero. */
max_size = 0;
}
/* Allocate our message */
messagelen = sizeof(outdata) + username_token.length + 1;
message = malloc(messagelen);
if(!message) {
gss_release_buffer(&unused_status, &username_token);
return CURLE_OUT_OF_MEMORY;
}
/* Populate the message with the security layer, client supported receive
message size and authorization identity including the 0x00 based
terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
identity is not terminated with the zero-valued (%x00) octet." it seems
necessary to include it. */
outdata = htonl(max_size) | sec_layer;
memcpy(message, &outdata, sizeof(outdata));
memcpy(message + sizeof(outdata), username_token.value,
username_token.length);
message[messagelen - 1] = '\0';
/* Free the username token as it is not required anymore */
gss_release_buffer(&unused_status, &username_token);
/* Setup the "authentication data" security buffer */
input_token.value = message;
input_token.length = messagelen;
/* Encrypt the data */
major_status = gss_wrap(&minor_status, krb5->context, 0,
GSS_C_QOP_DEFAULT, &input_token, NULL,
&output_token);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_wrap() failed: ",
major_status, minor_status);
free(message);
return CURLE_AUTH_ERROR;
}
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *) output_token.value,
output_token.length, outptr, outlen);
/* Free the output buffer */
gss_release_buffer(&unused_status, &output_token);
/* Free the message buffer */
free(message);
return result;
}
/*
* Curl_auth_cleanup_gssapi()
*
* This is used to clean up the GSSAPI (Kerberos V5) specific data.
*
* Parameters:
*
* krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
*
*/
void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
{
OM_uint32 minor_status;
/* Free our security context */
if(krb5->context != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER);
krb5->context = GSS_C_NO_CONTEXT;
}
/* Free the SPN */
if(krb5->spn != GSS_C_NO_NAME) {
gss_release_name(&minor_status, &krb5->spn);
krb5->spn = GSS_C_NO_NAME;
}
}
#endif /* HAVE_GSSAPI && USE_KERBEROS5 */

533
module/Vendor/CURL/lib/vauth/krb5_sspi.c vendored Normal file

@@ -0,0 +1,533 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014 - 2020, Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "curl_base64.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_gssapi_supported()
*
* This is used to evaluate if GSSAPI (Kerberos V5) is supported.
*
* Parameters: None
*
* Returns TRUE if Kerberos V5 is supported by Windows SSPI.
*/
bool Curl_auth_is_gssapi_supported(void)
{
PSecPkgInfo SecurityPackage;
SECURITY_STATUS status;
/* Query the security package for Kerberos */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_KERBEROS),
&SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
s_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
}
/*
* Curl_auth_create_gssapi_user_message()
*
* This is used to generate an already encoded GSSAPI (Kerberos V5) user token
* message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* mutual_auth [in] - Flag specifying whether or not mutual authentication
* is enabled.
* chlg64 [in] - The optional base64 encoded challenge message.
* krb5 [in/out] - The Kerberos 5 data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
const bool mutual_auth,
const char *chlg64,
struct kerberos5data *krb5,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
unsigned char *chlg = NULL;
CtxtHandle context;
PSecPkgInfo SecurityPackage;
SecBuffer chlg_buf;
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
SecBufferDesc resp_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
if(!krb5->spn) {
/* Generate our SPN */
krb5->spn = Curl_auth_build_spn(service, host, NULL);
if(!krb5->spn)
return CURLE_OUT_OF_MEMORY;
}
if(!krb5->output_token) {
/* Query the security package for Kerberos */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_KERBEROS),
&SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
krb5->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our response buffer */
krb5->output_token = malloc(krb5->token_max);
if(!krb5->output_token)
return CURLE_OUT_OF_MEMORY;
}
if(!krb5->credentials) {
/* Do we have credentials to use or are we using single sign-on? */
if(userp && *userp) {
/* Populate our identity structure */
result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
if(result)
return result;
/* Allow proper cleanup of the identity structure */
krb5->p_identity = &krb5->identity;
}
else
/* Use the current Windows user */
krb5->p_identity = NULL;
/* Allocate our credentials handle */
krb5->credentials = calloc(1, sizeof(CredHandle));
if(!krb5->credentials)
return CURLE_OUT_OF_MEMORY;
/* Acquire our credentials handle */
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *)
TEXT(SP_NAME_KERBEROS),
SECPKG_CRED_OUTBOUND, NULL,
krb5->p_identity, NULL, NULL,
krb5->credentials, &expiry);
if(status != SEC_E_OK)
return CURLE_LOGIN_DENIED;
/* Allocate our new context handle */
krb5->context = calloc(1, sizeof(CtxtHandle));
if(!krb5->context)
return CURLE_OUT_OF_MEMORY;
}
if(chlg64 && *chlg64) {
/* Decode the base-64 encoded challenge message */
if(*chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "GSSAPI handshake failure (empty challenge message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Setup the challenge "input" security buffer */
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 1;
chlg_desc.pBuffers = &chlg_buf;
chlg_buf.BufferType = SECBUFFER_TOKEN;
chlg_buf.pvBuffer = chlg;
chlg_buf.cbBuffer = curlx_uztoul(chlglen);
}
/* Setup the response "output" security buffer */
resp_desc.ulVersion = SECBUFFER_VERSION;
resp_desc.cBuffers = 1;
resp_desc.pBuffers = &resp_buf;
resp_buf.BufferType = SECBUFFER_TOKEN;
resp_buf.pvBuffer = krb5->output_token;
resp_buf.cbBuffer = curlx_uztoul(krb5->token_max);
/* Generate our challenge-response message */
status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
chlg ? krb5->context : NULL,
krb5->spn,
(mutual_auth ?
ISC_REQ_MUTUAL_AUTH : 0),
0, SECURITY_NATIVE_DREP,
chlg ? &chlg_desc : NULL, 0,
&context,
&resp_desc, &attrs,
&expiry);
/* Free the decoded challenge as it is not required anymore */
free(chlg);
if(status == SEC_E_INSUFFICIENT_MEMORY) {
return CURLE_OUT_OF_MEMORY;
}
if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
return CURLE_AUTH_ERROR;
}
if(memcmp(&context, krb5->context, sizeof(context))) {
s_pSecFn->DeleteSecurityContext(krb5->context);
memcpy(krb5->context, &context, sizeof(context));
}
if(resp_buf.cbBuffer) {
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *) resp_buf.pvBuffer,
resp_buf.cbBuffer, outptr, outlen);
}
else if(mutual_auth) {
*outptr = strdup("");
if(!*outptr)
result = CURLE_OUT_OF_MEMORY;
}
return result;
}
/*
* Curl_auth_create_gssapi_security_message()
*
* This is used to generate an already encoded GSSAPI (Kerberos V5) security
* token message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* chlg64 [in] - The optional base64 encoded challenge message.
* krb5 [in/out] - The Kerberos 5 data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
const char *chlg64,
struct kerberos5data *krb5,
char **outptr,
size_t *outlen)
{
CURLcode result = CURLE_OK;
size_t offset = 0;
size_t chlglen = 0;
size_t messagelen = 0;
size_t appdatalen = 0;
unsigned char *chlg = NULL;
unsigned char *trailer = NULL;
unsigned char *message = NULL;
unsigned char *padding = NULL;
unsigned char *appdata = NULL;
SecBuffer input_buf[2];
SecBuffer wrap_buf[3];
SecBufferDesc input_desc;
SecBufferDesc wrap_desc;
unsigned long indata = 0;
unsigned long outdata = 0;
unsigned long qop = 0;
unsigned long sec_layer = 0;
unsigned long max_size = 0;
SecPkgContext_Sizes sizes;
SecPkgCredentials_Names names;
SECURITY_STATUS status;
char *user_name;
/* Decode the base-64 encoded input message */
if(strlen(chlg64) && *chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "GSSAPI handshake failure (empty security message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Get our response size information */
status = s_pSecFn->QueryContextAttributes(krb5->context,
SECPKG_ATTR_SIZES,
&sizes);
if(status != SEC_E_OK) {
free(chlg);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
/* Get the fully qualified username back from the context */
status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
SECPKG_CRED_ATTR_NAMES,
&names);
if(status != SEC_E_OK) {
free(chlg);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
/* Setup the "input" security buffer */
input_desc.ulVersion = SECBUFFER_VERSION;
input_desc.cBuffers = 2;
input_desc.pBuffers = input_buf;
input_buf[0].BufferType = SECBUFFER_STREAM;
input_buf[0].pvBuffer = chlg;
input_buf[0].cbBuffer = curlx_uztoul(chlglen);
input_buf[1].BufferType = SECBUFFER_DATA;
input_buf[1].pvBuffer = NULL;
input_buf[1].cbBuffer = 0;
/* Decrypt the inbound challenge and obtain the qop */
status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
if(status != SEC_E_OK) {
infof(data, "GSSAPI handshake failure (empty security message)\n");
free(chlg);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Not 4 octets long so fail as per RFC4752 Section 3.1 */
if(input_buf[1].cbBuffer != 4) {
infof(data, "GSSAPI handshake failure (invalid security data)\n");
free(chlg);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Copy the data out and free the challenge as it is not required anymore */
memcpy(&indata, input_buf[1].pvBuffer, 4);
s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
free(chlg);
/* Extract the security layer */
sec_layer = indata & 0x000000FF;
if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
infof(data, "GSSAPI handshake failure (invalid security layer)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Extract the maximum message size the server can receive */
max_size = ntohl(indata & 0xFFFFFF00);
if(max_size > 0) {
/* The server has told us it supports a maximum receive buffer, however, as
we don't require one unless we are encrypting data, we tell the server
our receive buffer is zero. */
max_size = 0;
}
/* Allocate the trailer */
trailer = malloc(sizes.cbSecurityTrailer);
if(!trailer)
return CURLE_OUT_OF_MEMORY;
/* Convert the user name to UTF8 when operating with Unicode */
user_name = curlx_convert_tchar_to_UTF8(names.sUserName);
if(!user_name) {
free(trailer);
return CURLE_OUT_OF_MEMORY;
}
/* Allocate our message */
messagelen = sizeof(outdata) + strlen(user_name) + 1;
message = malloc(messagelen);
if(!message) {
free(trailer);
curlx_unicodefree(user_name);
return CURLE_OUT_OF_MEMORY;
}
/* Populate the message with the security layer, client supported receive
message size and authorization identity including the 0x00 based
terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
identity is not terminated with the zero-valued (%x00) octet." it seems
necessary to include it. */
outdata = htonl(max_size) | sec_layer;
memcpy(message, &outdata, sizeof(outdata));
strcpy((char *) message + sizeof(outdata), user_name);
curlx_unicodefree(user_name);
/* Allocate the padding */
padding = malloc(sizes.cbBlockSize);
if(!padding) {
free(message);
free(trailer);
return CURLE_OUT_OF_MEMORY;
}
/* Setup the "authentication data" security buffer */
wrap_desc.ulVersion = SECBUFFER_VERSION;
wrap_desc.cBuffers = 3;
wrap_desc.pBuffers = wrap_buf;
wrap_buf[0].BufferType = SECBUFFER_TOKEN;
wrap_buf[0].pvBuffer = trailer;
wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer;
wrap_buf[1].BufferType = SECBUFFER_DATA;
wrap_buf[1].pvBuffer = message;
wrap_buf[1].cbBuffer = curlx_uztoul(messagelen);
wrap_buf[2].BufferType = SECBUFFER_PADDING;
wrap_buf[2].pvBuffer = padding;
wrap_buf[2].cbBuffer = sizes.cbBlockSize;
/* Encrypt the data */
status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
&wrap_desc, 0);
if(status != SEC_E_OK) {
free(padding);
free(message);
free(trailer);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
/* Allocate the encryption (wrap) buffer */
appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
wrap_buf[2].cbBuffer;
appdata = malloc(appdatalen);
if(!appdata) {
free(padding);
free(message);
free(trailer);
return CURLE_OUT_OF_MEMORY;
}
/* Populate the encryption buffer */
memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
offset += wrap_buf[0].cbBuffer;
memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
offset += wrap_buf[1].cbBuffer;
memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *) appdata, appdatalen, outptr,
outlen);
/* Free all of our local buffers */
free(appdata);
free(padding);
free(message);
free(trailer);
return result;
}
/*
* Curl_auth_cleanup_gssapi()
*
* This is used to clean up the GSSAPI (Kerberos V5) specific data.
*
* Parameters:
*
* krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
*
*/
void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
{
/* Free our security context */
if(krb5->context) {
s_pSecFn->DeleteSecurityContext(krb5->context);
free(krb5->context);
krb5->context = NULL;
}
/* Free our credentials handle */
if(krb5->credentials) {
s_pSecFn->FreeCredentialsHandle(krb5->credentials);
free(krb5->credentials);
krb5->credentials = NULL;
}
/* Free our identity */
Curl_sspi_free_identity(krb5->p_identity);
krb5->p_identity = NULL;
/* Free the SPN and output token */
Curl_safefree(krb5->spn);
Curl_safefree(krb5->output_token);
/* Reset any variables */
krb5->token_max = 0;
}
#endif /* USE_WINDOWS_SSPI && USE_KERBEROS5*/

875
module/Vendor/CURL/lib/vauth/ntlm.c vendored Normal file

@@ -0,0 +1,875 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI)
/*
* NTLM details:
*
* https://davenport.sourceforge.io/ntlm.html
* https://www.innovation.ch/java/ntlm.html
*/
#define DEBUG_ME 0
#include "urldata.h"
#include "non-ascii.h"
#include "sendf.h"
#include "curl_base64.h"
#include "curl_ntlm_core.h"
#include "curl_gethostname.h"
#include "curl_multibyte.h"
#include "curl_md5.h"
#include "warnless.h"
#include "rand.h"
#include "vtls/vtls.h"
/* SSL backend-specific #if branches in this file must be kept in the order
documented in curl_ntlm_core. */
#if defined(NTLM_NEEDS_NSS_INIT)
#include "vtls/nssg.h" /* for Curl_nss_force_init() */
#endif
#define BUILDING_CURL_NTLM_MSGS_C
#include "vauth/vauth.h"
#include "vauth/ntlm.h"
#include "curl_endian.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/* "NTLMSSP" signature is always in ASCII regardless of the platform */
#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff))
#define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \
((int)(((x) >> 16) & 0xff)), ((int)(((x) >> 24) & 0xff))
#if DEBUG_ME
# define DEBUG_OUT(x) x
static void ntlm_print_flags(FILE *handle, unsigned long flags)
{
if(flags & NTLMFLAG_NEGOTIATE_UNICODE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE ");
if(flags & NTLMFLAG_NEGOTIATE_OEM)
fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM ");
if(flags & NTLMFLAG_REQUEST_TARGET)
fprintf(handle, "NTLMFLAG_REQUEST_TARGET ");
if(flags & (1<<3))
fprintf(handle, "NTLMFLAG_UNKNOWN_3 ");
if(flags & NTLMFLAG_NEGOTIATE_SIGN)
fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN ");
if(flags & NTLMFLAG_NEGOTIATE_SEAL)
fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL ");
if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE ");
if(flags & NTLMFLAG_NEGOTIATE_LM_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY ");
if(flags & NTLMFLAG_NEGOTIATE_NETWARE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_NETWARE ");
if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY ");
if(flags & (1<<10))
fprintf(handle, "NTLMFLAG_UNKNOWN_10 ");
if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS)
fprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS ");
if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED)
fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED ");
if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED)
fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED ");
if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL)
fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL ");
if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN)
fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN ");
if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN ");
if(flags & NTLMFLAG_TARGET_TYPE_SERVER)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER ");
if(flags & NTLMFLAG_TARGET_TYPE_SHARE)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE ");
if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY ");
if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE)
fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE ");
if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE)
fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE ");
if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY)
fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY ");
if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO)
fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO ");
if(flags & (1<<24))
fprintf(handle, "NTLMFLAG_UNKNOWN_24 ");
if(flags & (1<<25))
fprintf(handle, "NTLMFLAG_UNKNOWN_25 ");
if(flags & (1<<26))
fprintf(handle, "NTLMFLAG_UNKNOWN_26 ");
if(flags & (1<<27))
fprintf(handle, "NTLMFLAG_UNKNOWN_27 ");
if(flags & (1<<28))
fprintf(handle, "NTLMFLAG_UNKNOWN_28 ");
if(flags & NTLMFLAG_NEGOTIATE_128)
fprintf(handle, "NTLMFLAG_NEGOTIATE_128 ");
if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE ");
if(flags & NTLMFLAG_NEGOTIATE_56)
fprintf(handle, "NTLMFLAG_NEGOTIATE_56 ");
}
static void ntlm_print_hex(FILE *handle, const char *buf, size_t len)
{
const char *p = buf;
(void) handle;
fprintf(stderr, "0x");
while(len-- > 0)
fprintf(stderr, "%02.2x", (unsigned int)*p++);
}
#else
# define DEBUG_OUT(x) Curl_nop_stmt
#endif
/*
* ntlm_decode_type2_target()
*
* This is used to decode the "target info" in the NTLM type-2 message
* received.
*
* Parameters:
*
* data [in] - The session handle.
* buffer [in] - The decoded type-2 message.
* size [in] - The input buffer size, at least 32 bytes.
* ntlm [in/out] - The NTLM data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
static CURLcode ntlm_decode_type2_target(struct Curl_easy *data,
unsigned char *buffer,
size_t size,
struct ntlmdata *ntlm)
{
unsigned short target_info_len = 0;
unsigned int target_info_offset = 0;
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
if(size >= 48) {
target_info_len = Curl_read16_le(&buffer[40]);
target_info_offset = Curl_read32_le(&buffer[44]);
if(target_info_len > 0) {
if((target_info_offset >= size) ||
((target_info_offset + target_info_len) > size) ||
(target_info_offset < 48)) {
infof(data, "NTLM handshake failure (bad type-2 message). "
"Target Info Offset Len is set incorrect by the peer\n");
return CURLE_BAD_CONTENT_ENCODING;
}
free(ntlm->target_info); /* replace any previous data */
ntlm->target_info = malloc(target_info_len);
if(!ntlm->target_info)
return CURLE_OUT_OF_MEMORY;
memcpy(ntlm->target_info, &buffer[target_info_offset], target_info_len);
}
}
ntlm->target_info_len = target_info_len;
return CURLE_OK;
}
/*
NTLM message structure notes:
A 'short' is a 'network short', a little-endian 16-bit unsigned value.
A 'long' is a 'network long', a little-endian, 32-bit unsigned value.
A 'security buffer' represents a triplet used to point to a buffer,
consisting of two shorts and one long:
1. A 'short' containing the length of the buffer content in bytes.
2. A 'short' containing the allocated space for the buffer in bytes.
3. A 'long' containing the offset to the start of the buffer in bytes,
from the beginning of the NTLM message.
*/
/*
* Curl_auth_is_ntlm_supported()
*
* This is used to evaluate if NTLM is supported.
*
* Parameters: None
*
* Returns TRUE as NTLM as handled by libcurl.
*/
bool Curl_auth_is_ntlm_supported(void)
{
return TRUE;
}
/*
* Curl_auth_decode_ntlm_type2_message()
*
* This is used to decode an already encoded NTLM type-2 message. The message
* is first decoded from a base64 string into a raw NTLM message and checked
* for validity before the appropriate data for creating a type-3 message is
* written to the given NTLM data structure.
*
* Parameters:
*
* data [in] - The session handle.
* type2msg [in] - The base64 encoded type-2 message.
* ntlm [in/out] - The NTLM data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
const char *type2msg,
struct ntlmdata *ntlm)
{
static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 };
/* NTLM type-2 message structure:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x02000000)
12 Target Name security buffer
20 Flags long
24 Challenge 8 bytes
(32) Context 8 bytes (two consecutive longs) (*)
(40) Target Information security buffer (*)
(48) OS Version Structure 8 bytes (*)
32 (48) (56) Start of data block (*)
(*) -> Optional
*/
CURLcode result = CURLE_OK;
unsigned char *type2 = NULL;
size_t type2_len = 0;
#if defined(NTLM_NEEDS_NSS_INIT)
/* Make sure the crypto backend is initialized */
result = Curl_nss_force_init(data);
if(result)
return result;
#elif defined(CURL_DISABLE_VERBOSE_STRINGS)
(void)data;
#endif
/* Decode the base-64 encoded type-2 message */
if(strlen(type2msg) && *type2msg != '=') {
result = Curl_base64_decode(type2msg, &type2, &type2_len);
if(result)
return result;
}
/* Ensure we have a valid type-2 message */
if(!type2) {
infof(data, "NTLM handshake failure (empty type-2 message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
ntlm->flags = 0;
if((type2_len < 32) ||
(memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) ||
(memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) {
/* This was not a good enough type-2 message */
free(type2);
infof(data, "NTLM handshake failure (bad type-2 message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
ntlm->flags = Curl_read32_le(&type2[20]);
memcpy(ntlm->nonce, &type2[24], 8);
if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) {
result = ntlm_decode_type2_target(data, type2, type2_len, ntlm);
if(result) {
free(type2);
infof(data, "NTLM handshake failure (bad type-2 message)\n");
return result;
}
}
DEBUG_OUT({
fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags);
ntlm_print_flags(stderr, ntlm->flags);
fprintf(stderr, "\n nonce=");
ntlm_print_hex(stderr, (char *)ntlm->nonce, 8);
fprintf(stderr, "\n****\n");
fprintf(stderr, "**** Header %s\n ", header);
});
free(type2);
return result;
}
/* copy the source to the destination and fill in zeroes in every
other destination byte! */
static void unicodecpy(unsigned char *dest, const char *src, size_t length)
{
size_t i;
for(i = 0; i < length; i++) {
dest[2 * i] = (unsigned char)src[i];
dest[2 * i + 1] = '\0';
}
}
/*
* Curl_auth_create_ntlm_type1_message()
*
* This is used to generate an already encoded NTLM type-1 message ready for
* sending to the recipient using the appropriate compile time crypto API.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* ntlm [in/out] - The NTLM data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *hostname,
struct ntlmdata *ntlm,
char **outptr, size_t *outlen)
{
/* NTLM type-1 message structure:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x01000000)
12 Flags long
(16) Supplied Domain security buffer (*)
(24) Supplied Workstation security buffer (*)
(32) OS Version Structure 8 bytes (*)
(32) (40) Start of data block (*)
(*) -> Optional
*/
size_t size;
unsigned char ntlmbuf[NTLM_BUFSIZE];
const char *host = ""; /* empty */
const char *domain = ""; /* empty */
size_t hostlen = 0;
size_t domlen = 0;
size_t hostoff = 0;
size_t domoff = hostoff + hostlen; /* This is 0: remember that host and
domain are empty */
(void)userp;
(void)passwdp;
(void)service,
(void)hostname,
/* Clean up any former leftovers and initialise to defaults */
Curl_auth_cleanup_ntlm(ntlm);
#if defined(USE_NTRESPONSES) && defined(USE_NTLM2SESSION)
#define NTLM2FLAG NTLMFLAG_NEGOTIATE_NTLM2_KEY
#else
#define NTLM2FLAG 0
#endif
msnprintf((char *)ntlmbuf, NTLM_BUFSIZE,
NTLMSSP_SIGNATURE "%c"
"\x01%c%c%c" /* 32-bit type = 1 */
"%c%c%c%c" /* 32-bit NTLM flag field */
"%c%c" /* domain length */
"%c%c" /* domain allocated space */
"%c%c" /* domain name offset */
"%c%c" /* 2 zeroes */
"%c%c" /* host length */
"%c%c" /* host allocated space */
"%c%c" /* host name offset */
"%c%c" /* 2 zeroes */
"%s" /* host name */
"%s", /* domain string */
0, /* trailing zero */
0, 0, 0, /* part of type-1 long */
LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLM2FLAG |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0, 0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0, 0,
host, /* this is empty */
domain /* this is empty */);
/* Initial packet length */
size = 32 + hostlen + domlen;
DEBUG_OUT({
fprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x "
"0x%08.8x ",
LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLM2FLAG |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLM2FLAG |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
ntlm_print_flags(stderr,
NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLM2FLAG |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
fprintf(stderr, "\n****\n");
});
/* Return with binary blob encoded into base64 */
return Curl_base64_encode(data, (char *)ntlmbuf, size, outptr, outlen);
}
/*
* Curl_auth_create_ntlm_type3_message()
*
* This is used to generate an already encoded NTLM type-3 message ready for
* sending to the recipient using the appropriate compile time crypto API.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* ntlm [in/out] - The NTLM data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
char **outptr, size_t *outlen)
{
/* NTLM type-3 message structure:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x03000000)
12 LM/LMv2 Response security buffer
20 NTLM/NTLMv2 Response security buffer
28 Target Name security buffer
36 User Name security buffer
44 Workstation Name security buffer
(52) Session Key security buffer (*)
(60) Flags long (*)
(64) OS Version Structure 8 bytes (*)
52 (64) (72) Start of data block
(*) -> Optional
*/
CURLcode result = CURLE_OK;
size_t size;
unsigned char ntlmbuf[NTLM_BUFSIZE];
int lmrespoff;
unsigned char lmresp[24]; /* fixed-size */
#ifdef USE_NTRESPONSES
int ntrespoff;
unsigned int ntresplen = 24;
unsigned char ntresp[24]; /* fixed-size */
unsigned char *ptr_ntresp = &ntresp[0];
unsigned char *ntlmv2resp = NULL;
#endif
bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE;
char host[HOSTNAME_MAX + 1] = "";
const char *user;
const char *domain = "";
size_t hostoff = 0;
size_t useroff = 0;
size_t domoff = 0;
size_t hostlen = 0;
size_t userlen = 0;
size_t domlen = 0;
user = strchr(userp, '\\');
if(!user)
user = strchr(userp, '/');
if(user) {
domain = userp;
domlen = (user - domain);
user++;
}
else
user = userp;
userlen = strlen(user);
/* Get the machine's un-qualified host name as NTLM doesn't like the fully
qualified domain name */
if(Curl_gethostname(host, sizeof(host))) {
infof(data, "gethostname() failed, continuing without!\n");
hostlen = 0;
}
else {
hostlen = strlen(host);
}
#if defined(USE_NTRESPONSES) && defined(USE_NTLM_V2)
if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
unsigned char ntbuffer[0x18];
unsigned char entropy[8];
unsigned char ntlmv2hash[0x18];
result = Curl_rand(data, entropy, 8);
if(result)
return result;
result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer);
if(result)
return result;
result = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen,
ntbuffer, ntlmv2hash);
if(result)
return result;
/* LMv2 response */
result = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, entropy,
&ntlm->nonce[0], lmresp);
if(result)
return result;
/* NTLMv2 response */
result = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, entropy,
ntlm, &ntlmv2resp, &ntresplen);
if(result)
return result;
ptr_ntresp = ntlmv2resp;
}
else
#endif
#if defined(USE_NTRESPONSES) && defined(USE_NTLM2SESSION)
#define CURL_MD5_DIGEST_LENGTH 16 /* fixed size */
/* We don't support NTLM2 if we don't have USE_NTRESPONSES */
if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) {
unsigned char ntbuffer[0x18];
unsigned char tmp[0x18];
unsigned char md5sum[CURL_MD5_DIGEST_LENGTH];
unsigned char entropy[8];
/* Need to create 8 bytes random data */
result = Curl_rand(data, entropy, 8);
if(result)
return result;
/* 8 bytes random data as challenge in lmresp */
memcpy(lmresp, entropy, 8);
/* Pad with zeros */
memset(lmresp + 8, 0, 0x10);
/* Fill tmp with challenge(nonce?) + entropy */
memcpy(tmp, &ntlm->nonce[0], 8);
memcpy(tmp + 8, entropy, 8);
Curl_md5it(md5sum, tmp, 16);
/* We shall only use the first 8 bytes of md5sum, but the des code in
Curl_ntlm_core_lm_resp only encrypt the first 8 bytes */
result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer);
if(result)
return result;
Curl_ntlm_core_lm_resp(ntbuffer, md5sum, ntresp);
/* End of NTLM2 Session code */
/* NTLM v2 session security is a misnomer because it is not NTLM v2.
It is NTLM v1 using the extended session security that is also
in NTLM v2 */
}
else
#endif
{
#ifdef USE_NTRESPONSES
unsigned char ntbuffer[0x18];
#endif
unsigned char lmbuffer[0x18];
#ifdef USE_NTRESPONSES
result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer);
if(result)
return result;
Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
#endif
result = Curl_ntlm_core_mk_lm_hash(data, passwdp, lmbuffer);
if(result)
return result;
Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
/* A safer but less compatible alternative is:
* Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
* See https://davenport.sourceforge.io/ntlm.html#ntlmVersion2 */
}
if(unicode) {
domlen = domlen * 2;
userlen = userlen * 2;
hostlen = hostlen * 2;
}
lmrespoff = 64; /* size of the message header */
#ifdef USE_NTRESPONSES
ntrespoff = lmrespoff + 0x18;
domoff = ntrespoff + ntresplen;
#else
domoff = lmrespoff + 0x18;
#endif
useroff = domoff + domlen;
hostoff = useroff + userlen;
/* Create the big type-3 message binary blob */
size = msnprintf((char *)ntlmbuf, NTLM_BUFSIZE,
NTLMSSP_SIGNATURE "%c"
"\x03%c%c%c" /* 32-bit type = 3 */
"%c%c" /* LanManager length */
"%c%c" /* LanManager allocated space */
"%c%c" /* LanManager offset */
"%c%c" /* 2 zeroes */
"%c%c" /* NT-response length */
"%c%c" /* NT-response allocated space */
"%c%c" /* NT-response offset */
"%c%c" /* 2 zeroes */
"%c%c" /* domain length */
"%c%c" /* domain allocated space */
"%c%c" /* domain name offset */
"%c%c" /* 2 zeroes */
"%c%c" /* user length */
"%c%c" /* user allocated space */
"%c%c" /* user offset */
"%c%c" /* 2 zeroes */
"%c%c" /* host length */
"%c%c" /* host allocated space */
"%c%c" /* host offset */
"%c%c" /* 2 zeroes */
"%c%c" /* session key length (unknown purpose) */
"%c%c" /* session key allocated space (unknown purpose) */
"%c%c" /* session key offset (unknown purpose) */
"%c%c" /* 2 zeroes */
"%c%c%c%c", /* flags */
/* domain string */
/* user string */
/* host string */
/* LanManager response */
/* NT response */
0, /* zero termination */
0, 0, 0, /* type-3 long, the 24 upper bits */
SHORTPAIR(0x18), /* LanManager response length, twice */
SHORTPAIR(0x18),
SHORTPAIR(lmrespoff),
0x0, 0x0,
#ifdef USE_NTRESPONSES
SHORTPAIR(ntresplen), /* NT-response length, twice */
SHORTPAIR(ntresplen),
SHORTPAIR(ntrespoff),
0x0, 0x0,
#else
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
#endif
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0x0, 0x0,
SHORTPAIR(userlen),
SHORTPAIR(userlen),
SHORTPAIR(useroff),
0x0, 0x0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
LONGQUARTET(ntlm->flags));
DEBUGASSERT(size == 64);
DEBUGASSERT(size == (size_t)lmrespoff);
/* We append the binary hashes */
if(size < (NTLM_BUFSIZE - 0x18)) {
memcpy(&ntlmbuf[size], lmresp, 0x18);
size += 0x18;
}
DEBUG_OUT({
fprintf(stderr, "**** TYPE3 header lmresp=");
ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18);
});
#ifdef USE_NTRESPONSES
/* ntresplen + size should not be risking an integer overflow here */
if(ntresplen + size > sizeof(ntlmbuf)) {
failf(data, "incoming NTLM message too big");
return CURLE_OUT_OF_MEMORY;
}
DEBUGASSERT(size == (size_t)ntrespoff);
memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen);
size += ntresplen;
DEBUG_OUT({
fprintf(stderr, "\n ntresp=");
ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen);
});
free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */
#endif
DEBUG_OUT({
fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
LONGQUARTET(ntlm->flags), ntlm->flags);
ntlm_print_flags(stderr, ntlm->flags);
fprintf(stderr, "\n****\n");
});
/* Make sure that the domain, user and host strings fit in the
buffer before we copy them there. */
if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) {
failf(data, "user + domain + host name too big");
return CURLE_OUT_OF_MEMORY;
}
DEBUGASSERT(size == domoff);
if(unicode)
unicodecpy(&ntlmbuf[size], domain, domlen / 2);
else
memcpy(&ntlmbuf[size], domain, domlen);
size += domlen;
DEBUGASSERT(size == useroff);
if(unicode)
unicodecpy(&ntlmbuf[size], user, userlen / 2);
else
memcpy(&ntlmbuf[size], user, userlen);
size += userlen;
DEBUGASSERT(size == hostoff);
if(unicode)
unicodecpy(&ntlmbuf[size], host, hostlen / 2);
else
memcpy(&ntlmbuf[size], host, hostlen);
size += hostlen;
/* Convert domain, user, and host to ASCII but leave the rest as-is */
result = Curl_convert_to_network(data, (char *)&ntlmbuf[domoff],
size - domoff);
if(result)
return CURLE_CONV_FAILED;
/* Return with binary blob encoded into base64 */
result = Curl_base64_encode(data, (char *)ntlmbuf, size, outptr, outlen);
Curl_auth_cleanup_ntlm(ntlm);
return result;
}
/*
* Curl_auth_cleanup_ntlm()
*
* This is used to clean up the NTLM specific data.
*
* Parameters:
*
* ntlm [in/out] - The NTLM data struct being cleaned up.
*
*/
void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
{
/* Free the target info */
Curl_safefree(ntlm->target_info);
/* Reset any variables */
ntlm->target_info_len = 0;
}
#endif /* USE_NTLM && !USE_WINDOWS_SSPI */

143
module/Vendor/CURL/lib/vauth/ntlm.h vendored Normal file

@@ -0,0 +1,143 @@
#ifndef HEADER_VAUTH_NTLM_H
#define HEADER_VAUTH_NTLM_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_NTLM
/* NTLM buffer fixed size, large enough for long user + host + domain */
#define NTLM_BUFSIZE 1024
/* Stuff only required for curl_ntlm_msgs.c */
#ifdef BUILDING_CURL_NTLM_MSGS_C
/* Flag bits definitions based on https://davenport.sourceforge.io/ntlm.html */
#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
/* Indicates that Unicode strings are supported for use in security buffer
data. */
#define NTLMFLAG_NEGOTIATE_OEM (1<<1)
/* Indicates that OEM strings are supported for use in security buffer data. */
#define NTLMFLAG_REQUEST_TARGET (1<<2)
/* Requests that the server's authentication realm be included in the Type 2
message. */
/* unknown (1<<3) */
#define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
/* Specifies that authenticated communication between the client and server
should carry a digital signature (message integrity). */
#define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
/* Specifies that authenticated communication between the client and server
should be encrypted (message confidentiality). */
#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
/* Indicates that datagram authentication is being used. */
#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
/* Indicates that the LAN Manager session key should be used for signing and
sealing authenticated communications. */
#define NTLMFLAG_NEGOTIATE_NETWARE (1<<8)
/* unknown purpose */
#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
/* Indicates that NTLM authentication is being used. */
/* unknown (1<<10) */
#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11)
/* Sent by the client in the Type 3 message to indicate that an anonymous
context has been established. This also affects the response fields. */
#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
/* Sent by the client in the Type 1 message to indicate that a desired
authentication realm is included in the message. */
#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
/* Sent by the client in the Type 1 message to indicate that the client
workstation's name is included in the message. */
#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
/* Sent by the server to indicate that the server and client are on the same
machine. Implies that the client may use a pre-established local security
context rather than responding to the challenge. */
#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
/* Indicates that authenticated communication between the client and server
should be signed with a "dummy" signature. */
#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a domain. */
#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a server. */
#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a share. Presumably, this is for share-level
authentication. Usage is unclear. */
#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
/* Indicates that the NTLM2 signing and sealing scheme should be used for
protecting authenticated communications. */
#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
/* unknown purpose */
#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
/* unknown purpose */
#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
/* unknown purpose */
#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
/* Sent by the server in the Type 2 message to indicate that it is including a
Target Information block in the message. */
/* unknown (1<24) */
/* unknown (1<25) */
/* unknown (1<26) */
/* unknown (1<27) */
/* unknown (1<28) */
#define NTLMFLAG_NEGOTIATE_128 (1<<29)
/* Indicates that 128-bit encryption is supported. */
#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
/* Indicates that the client will provide an encrypted master key in
the "Session Key" field of the Type 3 message. */
#define NTLMFLAG_NEGOTIATE_56 (1<<31)
/* Indicates that 56-bit encryption is supported. */
#endif /* BUILDING_CURL_NTLM_MSGS_C */
#endif /* USE_NTLM */
#endif /* HEADER_VAUTH_NTLM_H */

383
module/Vendor/CURL/lib/vauth/ntlm_sspi.c vendored Normal file

@@ -0,0 +1,383 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "curl_base64.h"
#include "curl_ntlm_core.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_ntlm_supported()
*
* This is used to evaluate if NTLM is supported.
*
* Parameters: None
*
* Returns TRUE if NTLM is supported by Windows SSPI.
*/
bool Curl_auth_is_ntlm_supported(void)
{
PSecPkgInfo SecurityPackage;
SECURITY_STATUS status;
/* Query the security package for NTLM */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
&SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
s_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
}
/*
* Curl_auth_create_ntlm_type1_message()
*
* This is used to generate an already encoded NTLM type-1 message ready for
* sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* ntlm [in/out] - The NTLM data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
struct ntlmdata *ntlm,
char **outptr, size_t *outlen)
{
PSecPkgInfo SecurityPackage;
SecBuffer type_1_buf;
SecBufferDesc type_1_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
/* Clean up any former leftovers and initialise to defaults */
Curl_auth_cleanup_ntlm(ntlm);
/* Query the security package for NTLM */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
&SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
ntlm->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our output buffer */
ntlm->output_token = malloc(ntlm->token_max);
if(!ntlm->output_token)
return CURLE_OUT_OF_MEMORY;
if(userp && *userp) {
CURLcode result;
/* Populate our identity structure */
result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
if(result)
return result;
/* Allow proper cleanup of the identity structure */
ntlm->p_identity = &ntlm->identity;
}
else
/* Use the current Windows user */
ntlm->p_identity = NULL;
/* Allocate our credentials handle */
ntlm->credentials = calloc(1, sizeof(CredHandle));
if(!ntlm->credentials)
return CURLE_OUT_OF_MEMORY;
/* Acquire our credentials handle */
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_NTLM),
SECPKG_CRED_OUTBOUND, NULL,
ntlm->p_identity, NULL, NULL,
ntlm->credentials, &expiry);
if(status != SEC_E_OK)
return CURLE_LOGIN_DENIED;
/* Allocate our new context handle */
ntlm->context = calloc(1, sizeof(CtxtHandle));
if(!ntlm->context)
return CURLE_OUT_OF_MEMORY;
ntlm->spn = Curl_auth_build_spn(service, host, NULL);
if(!ntlm->spn)
return CURLE_OUT_OF_MEMORY;
/* Setup the type-1 "output" security buffer */
type_1_desc.ulVersion = SECBUFFER_VERSION;
type_1_desc.cBuffers = 1;
type_1_desc.pBuffers = &type_1_buf;
type_1_buf.BufferType = SECBUFFER_TOKEN;
type_1_buf.pvBuffer = ntlm->output_token;
type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
/* Generate our type-1 message */
status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
ntlm->spn,
0, 0, SECURITY_NETWORK_DREP,
NULL, 0,
ntlm->context, &type_1_desc,
&attrs, &expiry);
if(status == SEC_I_COMPLETE_NEEDED ||
status == SEC_I_COMPLETE_AND_CONTINUE)
s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
else if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
return CURLE_AUTH_ERROR;
/* Base64 encode the response */
return Curl_base64_encode(data, (char *) ntlm->output_token,
type_1_buf.cbBuffer, outptr, outlen);
}
/*
* Curl_auth_decode_ntlm_type2_message()
*
* This is used to decode an already encoded NTLM type-2 message.
*
* Parameters:
*
* data [in] - The session handle.
* type2msg [in] - The base64 encoded type-2 message.
* ntlm [in/out] - The NTLM data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
const char *type2msg,
struct ntlmdata *ntlm)
{
CURLcode result = CURLE_OK;
unsigned char *type2 = NULL;
size_t type2_len = 0;
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
/* Decode the base-64 encoded type-2 message */
if(strlen(type2msg) && *type2msg != '=') {
result = Curl_base64_decode(type2msg, &type2, &type2_len);
if(result)
return result;
}
/* Ensure we have a valid type-2 message */
if(!type2) {
infof(data, "NTLM handshake failure (empty type-2 message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Simply store the challenge for use later */
ntlm->input_token = type2;
ntlm->input_token_len = type2_len;
return result;
}
/*
* Curl_auth_create_ntlm_type3_message()
* Curl_auth_create_ntlm_type3_message()
*
* This is used to generate an already encoded NTLM type-3 message ready for
* sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* ntlm [in/out] - The NTLM data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
SecBuffer type_2_bufs[2];
SecBuffer type_3_buf;
SecBufferDesc type_2_desc;
SecBufferDesc type_3_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
(void) passwdp;
(void) userp;
/* Setup the type-2 "input" security buffer */
type_2_desc.ulVersion = SECBUFFER_VERSION;
type_2_desc.cBuffers = 1;
type_2_desc.pBuffers = &type_2_bufs[0];
type_2_bufs[0].BufferType = SECBUFFER_TOKEN;
type_2_bufs[0].pvBuffer = ntlm->input_token;
type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len);
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
/* ssl context comes from schannel.
* When extended protection is used in IIS server,
* we have to pass a second SecBuffer to the SecBufferDesc
* otherwise IIS will not pass the authentication (401 response).
* Minimum supported version is Windows 7.
* https://docs.microsoft.com/en-us/security-updates
* /SecurityAdvisories/2009/973811
*/
if(ntlm->sslContext) {
SEC_CHANNEL_BINDINGS channelBindings;
SecPkgContext_Bindings pkgBindings;
pkgBindings.Bindings = &channelBindings;
status = s_pSecFn->QueryContextAttributes(
ntlm->sslContext,
SECPKG_ATTR_ENDPOINT_BINDINGS,
&pkgBindings
);
if(status == SEC_E_OK) {
type_2_desc.cBuffers++;
type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength;
type_2_bufs[1].pvBuffer = pkgBindings.Bindings;
}
}
#endif
/* Setup the type-3 "output" security buffer */
type_3_desc.ulVersion = SECBUFFER_VERSION;
type_3_desc.cBuffers = 1;
type_3_desc.pBuffers = &type_3_buf;
type_3_buf.BufferType = SECBUFFER_TOKEN;
type_3_buf.pvBuffer = ntlm->output_token;
type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
/* Generate our type-3 message */
status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
ntlm->context,
ntlm->spn,
0, 0, SECURITY_NETWORK_DREP,
&type_2_desc,
0, ntlm->context,
&type_3_desc,
&attrs, &expiry);
if(status != SEC_E_OK) {
infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
status);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *) ntlm->output_token,
type_3_buf.cbBuffer, outptr, outlen);
Curl_auth_cleanup_ntlm(ntlm);
return result;
}
/*
* Curl_auth_cleanup_ntlm()
*
* This is used to clean up the NTLM specific data.
*
* Parameters:
*
* ntlm [in/out] - The NTLM data struct being cleaned up.
*
*/
void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
{
/* Free our security context */
if(ntlm->context) {
s_pSecFn->DeleteSecurityContext(ntlm->context);
free(ntlm->context);
ntlm->context = NULL;
}
/* Free our credentials handle */
if(ntlm->credentials) {
s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
free(ntlm->credentials);
ntlm->credentials = NULL;
}
/* Free our identity */
Curl_sspi_free_identity(ntlm->p_identity);
ntlm->p_identity = NULL;
/* Free the input and output tokens */
Curl_safefree(ntlm->input_token);
Curl_safefree(ntlm->output_token);
/* Reset any variables */
ntlm->token_max = 0;
Curl_safefree(ntlm->spn);
}
#endif /* USE_WINDOWS_SSPI && USE_NTLM */

126
module/Vendor/CURL/lib/vauth/oauth2.c vendored Normal file

@@ -0,0 +1,126 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC6749 OAuth 2.0 Authorization Framework
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
!defined(CURL_DISABLE_POP3)
#include <curl/curl.h>
#include "urldata.h"
#include "vauth/vauth.h"
#include "curl_base64.h"
#include "warnless.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_create_oauth_bearer_message()
*
* This is used to generate an already encoded OAuth 2.0 message ready for
* sending to the recipient.
*
* Parameters:
*
* data[in] - The session handle.
* user[in] - The user name.
* host[in] - The host name.
* port[in] - The port(when not Port 80).
* bearer[in] - The bearer token.
* outptr[in / out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen[out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_easy *data,
const char *user,
const char *host,
const long port,
const char *bearer,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
char *oauth = NULL;
/* Generate the message */
if(port == 0 || port == 80)
oauth = aprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host,
bearer);
else
oauth = aprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", user,
host, port, bearer);
if(!oauth)
return CURLE_OUT_OF_MEMORY;
/* Base64 encode the reply */
result = Curl_base64_encode(data, oauth, strlen(oauth), outptr, outlen);
free(oauth);
return result;
}
/*
* Curl_auth_create_xoauth_bearer_message()
*
* This is used to generate an already encoded XOAuth 2.0 message ready for
* sending to the recipient.
*
* Parameters:
*
* data[in] - The session handle.
* user[in] - The user name.
* bearer[in] - The bearer token.
* outptr[in / out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen[out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_easy *data,
const char *user,
const char *bearer,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
/* Generate the message */
char *xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
if(!xoauth)
return CURLE_OUT_OF_MEMORY;
/* Base64 encode the reply */
result = Curl_base64_encode(data, xoauth, strlen(xoauth), outptr, outlen);
free(xoauth);
return result;
}
#endif /* disabled, no users */

@@ -0,0 +1,282 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC4178 Simple and Protected GSS-API Negotiation Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(HAVE_GSSAPI) && defined(USE_SPNEGO)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "curl_base64.h"
#include "curl_gssapi.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_spnego_supported()
*
* This is used to evaluate if SPNEGO (Negotiate) is supported.
*
* Parameters: None
*
* Returns TRUE if Negotiate supported by the GSS-API library.
*/
bool Curl_auth_is_spnego_supported(void)
{
return TRUE;
}
/*
* Curl_auth_decode_spnego_message()
*
* This is used to decode an already encoded SPNEGO (Negotiate) challenge
* message.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* chlg64 [in] - The optional base64 encoded challenge message.
* nego [in/out] - The Negotiate data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
const char *user,
const char *password,
const char *service,
const char *host,
const char *chlg64,
struct negotiatedata *nego)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
unsigned char *chlg = NULL;
OM_uint32 major_status;
OM_uint32 minor_status;
OM_uint32 unused_status;
gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
(void) user;
(void) password;
if(nego->context && nego->status == GSS_S_COMPLETE) {
/* We finished successfully our part of authentication, but server
* rejected it (since we're again here). Exit with an error since we
* can't invent anything better */
Curl_auth_cleanup_spnego(nego);
return CURLE_LOGIN_DENIED;
}
if(!nego->spn) {
/* Generate our SPN */
char *spn = Curl_auth_build_spn(service, NULL, host);
if(!spn)
return CURLE_OUT_OF_MEMORY;
/* Populate the SPN structure */
spn_token.value = spn;
spn_token.length = strlen(spn);
/* Import the SPN */
major_status = gss_import_name(&minor_status, &spn_token,
GSS_C_NT_HOSTBASED_SERVICE,
&nego->spn);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_import_name() failed: ",
major_status, minor_status);
free(spn);
return CURLE_AUTH_ERROR;
}
free(spn);
}
if(chlg64 && *chlg64) {
/* Decode the base-64 encoded challenge message */
if(*chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "SPNEGO handshake failure (empty challenge message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Setup the challenge "input" security buffer */
input_token.value = chlg;
input_token.length = chlglen;
}
/* Generate our challenge-response message */
major_status = Curl_gss_init_sec_context(data,
&minor_status,
&nego->context,
nego->spn,
&Curl_spnego_mech_oid,
GSS_C_NO_CHANNEL_BINDINGS,
&input_token,
&output_token,
TRUE,
NULL);
/* Free the decoded challenge as it is not required anymore */
Curl_safefree(input_token.value);
nego->status = major_status;
if(GSS_ERROR(major_status)) {
if(output_token.value)
gss_release_buffer(&unused_status, &output_token);
Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
major_status, minor_status);
return CURLE_AUTH_ERROR;
}
if(!output_token.value || !output_token.length) {
if(output_token.value)
gss_release_buffer(&unused_status, &output_token);
return CURLE_AUTH_ERROR;
}
/* Free previous token */
if(nego->output_token.length && nego->output_token.value)
gss_release_buffer(&unused_status, &nego->output_token);
nego->output_token = output_token;
return CURLE_OK;
}
/*
* Curl_auth_create_spnego_message()
*
* This is used to generate an already encoded SPNEGO (Negotiate) response
* message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* nego [in/out] - The Negotiate data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
struct negotiatedata *nego,
char **outptr, size_t *outlen)
{
CURLcode result;
OM_uint32 minor_status;
/* Base64 encode the already generated response */
result = Curl_base64_encode(data,
nego->output_token.value,
nego->output_token.length,
outptr, outlen);
if(result) {
gss_release_buffer(&minor_status, &nego->output_token);
nego->output_token.value = NULL;
nego->output_token.length = 0;
return result;
}
if(!*outptr || !*outlen) {
gss_release_buffer(&minor_status, &nego->output_token);
nego->output_token.value = NULL;
nego->output_token.length = 0;
return CURLE_REMOTE_ACCESS_DENIED;
}
return CURLE_OK;
}
/*
* Curl_auth_cleanup_spnego()
*
* This is used to clean up the SPNEGO (Negotiate) specific data.
*
* Parameters:
*
* nego [in/out] - The Negotiate data struct being cleaned up.
*
*/
void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
{
OM_uint32 minor_status;
/* Free our security context */
if(nego->context != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER);
nego->context = GSS_C_NO_CONTEXT;
}
/* Free the output token */
if(nego->output_token.value) {
gss_release_buffer(&minor_status, &nego->output_token);
nego->output_token.value = NULL;
nego->output_token.length = 0;
}
/* Free the SPN */
if(nego->spn != GSS_C_NO_NAME) {
gss_release_name(&minor_status, &nego->spn);
nego->spn = GSS_C_NO_NAME;
}
/* Reset any variables */
nego->status = 0;
nego->noauthpersist = FALSE;
nego->havenoauthpersist = FALSE;
nego->havenegdata = FALSE;
nego->havemultiplerequests = FALSE;
}
#endif /* HAVE_GSSAPI && USE_SPNEGO */

@@ -0,0 +1,371 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC4178 Simple and Protected GSS-API Negotiation Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "curl_base64.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
#include "strerror.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_spnego_supported()
*
* This is used to evaluate if SPNEGO (Negotiate) is supported.
*
* Parameters: None
*
* Returns TRUE if Negotiate is supported by Windows SSPI.
*/
bool Curl_auth_is_spnego_supported(void)
{
PSecPkgInfo SecurityPackage;
SECURITY_STATUS status;
/* Query the security package for Negotiate */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_NEGOTIATE),
&SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
s_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
}
/*
* Curl_auth_decode_spnego_message()
*
* This is used to decode an already encoded SPNEGO (Negotiate) challenge
* message.
*
* Parameters:
*
* data [in] - The session handle.
* user [in] - The user name in the format User or Domain\User.
* password [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* chlg64 [in] - The optional base64 encoded challenge message.
* nego [in/out] - The Negotiate data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
const char *user,
const char *password,
const char *service,
const char *host,
const char *chlg64,
struct negotiatedata *nego)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
unsigned char *chlg = NULL;
PSecPkgInfo SecurityPackage;
SecBuffer chlg_buf[2];
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
SecBufferDesc resp_desc;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
if(nego->context && nego->status == SEC_E_OK) {
/* We finished successfully our part of authentication, but server
* rejected it (since we're again here). Exit with an error since we
* can't invent anything better */
Curl_auth_cleanup_spnego(nego);
return CURLE_LOGIN_DENIED;
}
if(!nego->spn) {
/* Generate our SPN */
nego->spn = Curl_auth_build_spn(service, host, NULL);
if(!nego->spn)
return CURLE_OUT_OF_MEMORY;
}
if(!nego->output_token) {
/* Query the security package for Negotiate */
nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_NEGOTIATE),
&SecurityPackage);
if(nego->status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
nego->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our output buffer */
nego->output_token = malloc(nego->token_max);
if(!nego->output_token)
return CURLE_OUT_OF_MEMORY;
}
if(!nego->credentials) {
/* Do we have credentials to use or are we using single sign-on? */
if(user && *user) {
/* Populate our identity structure */
result = Curl_create_sspi_identity(user, password, &nego->identity);
if(result)
return result;
/* Allow proper cleanup of the identity structure */
nego->p_identity = &nego->identity;
}
else
/* Use the current Windows user */
nego->p_identity = NULL;
/* Allocate our credentials handle */
nego->credentials = calloc(1, sizeof(CredHandle));
if(!nego->credentials)
return CURLE_OUT_OF_MEMORY;
/* Acquire our credentials handle */
nego->status =
s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *)TEXT(SP_NAME_NEGOTIATE),
SECPKG_CRED_OUTBOUND, NULL,
nego->p_identity, NULL, NULL,
nego->credentials, &expiry);
if(nego->status != SEC_E_OK)
return CURLE_AUTH_ERROR;
/* Allocate our new context handle */
nego->context = calloc(1, sizeof(CtxtHandle));
if(!nego->context)
return CURLE_OUT_OF_MEMORY;
}
if(chlg64 && *chlg64) {
/* Decode the base-64 encoded challenge message */
if(*chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "SPNEGO handshake failure (empty challenge message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Setup the challenge "input" security buffer */
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 1;
chlg_desc.pBuffers = &chlg_buf[0];
chlg_buf[0].BufferType = SECBUFFER_TOKEN;
chlg_buf[0].pvBuffer = chlg;
chlg_buf[0].cbBuffer = curlx_uztoul(chlglen);
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
/* ssl context comes from Schannel.
* When extended protection is used in IIS server,
* we have to pass a second SecBuffer to the SecBufferDesc
* otherwise IIS will not pass the authentication (401 response).
* Minimum supported version is Windows 7.
* https://docs.microsoft.com/en-us/security-updates
* /SecurityAdvisories/2009/973811
*/
if(nego->sslContext) {
SEC_CHANNEL_BINDINGS channelBindings;
SecPkgContext_Bindings pkgBindings;
pkgBindings.Bindings = &channelBindings;
nego->status = s_pSecFn->QueryContextAttributes(
nego->sslContext,
SECPKG_ATTR_ENDPOINT_BINDINGS,
&pkgBindings
);
if(nego->status == SEC_E_OK) {
chlg_desc.cBuffers++;
chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
chlg_buf[1].cbBuffer = pkgBindings.BindingsLength;
chlg_buf[1].pvBuffer = pkgBindings.Bindings;
}
}
#endif
}
/* Setup the response "output" security buffer */
resp_desc.ulVersion = SECBUFFER_VERSION;
resp_desc.cBuffers = 1;
resp_desc.pBuffers = &resp_buf;
resp_buf.BufferType = SECBUFFER_TOKEN;
resp_buf.pvBuffer = nego->output_token;
resp_buf.cbBuffer = curlx_uztoul(nego->token_max);
/* Generate our challenge-response message */
nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials,
chlg ? nego->context :
NULL,
nego->spn,
ISC_REQ_CONFIDENTIALITY,
0, SECURITY_NATIVE_DREP,
chlg ? &chlg_desc : NULL,
0, nego->context,
&resp_desc, &attrs,
&expiry);
/* Free the decoded challenge as it is not required anymore */
free(chlg);
if(GSS_ERROR(nego->status)) {
char buffer[STRERROR_LEN];
failf(data, "InitializeSecurityContext failed: %s",
Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
if(nego->status == SEC_I_COMPLETE_NEEDED ||
nego->status == SEC_I_COMPLETE_AND_CONTINUE) {
nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc);
if(GSS_ERROR(nego->status)) {
char buffer[STRERROR_LEN];
failf(data, "CompleteAuthToken failed: %s",
Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
}
nego->output_token_length = resp_buf.cbBuffer;
return result;
}
/*
* Curl_auth_create_spnego_message()
*
* This is used to generate an already encoded SPNEGO (Negotiate) response
* message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* nego [in/out] - The Negotiate data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
struct negotiatedata *nego,
char **outptr, size_t *outlen)
{
CURLcode result;
/* Base64 encode the already generated response */
result = Curl_base64_encode(data,
(const char *) nego->output_token,
nego->output_token_length,
outptr, outlen);
if(result)
return result;
if(!*outptr || !*outlen) {
free(*outptr);
return CURLE_REMOTE_ACCESS_DENIED;
}
return CURLE_OK;
}
/*
* Curl_auth_cleanup_spnego()
*
* This is used to clean up the SPNEGO (Negotiate) specific data.
*
* Parameters:
*
* nego [in/out] - The Negotiate data struct being cleaned up.
*
*/
void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
{
/* Free our security context */
if(nego->context) {
s_pSecFn->DeleteSecurityContext(nego->context);
free(nego->context);
nego->context = NULL;
}
/* Free our credentials handle */
if(nego->credentials) {
s_pSecFn->FreeCredentialsHandle(nego->credentials);
free(nego->credentials);
nego->credentials = NULL;
}
/* Free our identity */
Curl_sspi_free_identity(nego->p_identity);
nego->p_identity = NULL;
/* Free the SPN and output token */
Curl_safefree(nego->spn);
Curl_safefree(nego->output_token);
/* Reset any variables */
nego->status = 0;
nego->token_max = 0;
nego->noauthpersist = FALSE;
nego->havenoauthpersist = FALSE;
nego->havenegdata = FALSE;
nego->havemultiplerequests = FALSE;
}
#endif /* USE_WINDOWS_SSPI && USE_SPNEGO */

147
module/Vendor/CURL/lib/vauth/vauth.c vendored Normal file

@@ -0,0 +1,147 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014 - 2020, Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "vauth.h"
#include "curl_multibyte.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_build_spn()
*
* This is used to build a SPN string in the following formats:
*
* service/host@realm (Not currently used)
* service/host (Not used by GSS-API)
* service@realm (Not used by Windows SSPI)
*
* Parameters:
*
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* realm [in] - The realm.
*
* Returns a pointer to the newly allocated SPN.
*/
#if !defined(USE_WINDOWS_SSPI)
char *Curl_auth_build_spn(const char *service, const char *host,
const char *realm)
{
char *spn = NULL;
/* Generate our SPN */
if(host && realm)
spn = aprintf("%s/%s@%s", service, host, realm);
else if(host)
spn = aprintf("%s/%s", service, host);
else if(realm)
spn = aprintf("%s@%s", service, realm);
/* Return our newly allocated SPN */
return spn;
}
#else
TCHAR *Curl_auth_build_spn(const char *service, const char *host,
const char *realm)
{
char *utf8_spn = NULL;
TCHAR *tchar_spn = NULL;
(void) realm;
/* Note: We could use DsMakeSPN() or DsClientMakeSpnForTargetServer() rather
than doing this ourselves but the first is only available in Windows XP
and Windows Server 2003 and the latter is only available in Windows 2000
but not Windows95/98/ME or Windows NT4.0 unless the Active Directory
Client Extensions are installed. As such it is far simpler for us to
formulate the SPN instead. */
/* Generate our UTF8 based SPN */
utf8_spn = aprintf("%s/%s", service, host);
if(!utf8_spn) {
return NULL;
}
/* Allocate our TCHAR based SPN */
tchar_spn = curlx_convert_UTF8_to_tchar(utf8_spn);
if(!tchar_spn) {
free(utf8_spn);
return NULL;
}
/* Release the UTF8 variant when operating with Unicode */
curlx_unicodefree(utf8_spn);
/* Return our newly allocated SPN */
return tchar_spn;
}
#endif /* USE_WINDOWS_SSPI */
/*
* Curl_auth_user_contains_domain()
*
* This is used to test if the specified user contains a Windows domain name as
* follows:
*
* Domain\User (Down-level Logon Name)
* Domain/User (curl Down-level format - for compatibility with existing code)
* User@Domain (User Principal Name)
*
* Note: The user name may be empty when using a GSS-API library or Windows
* SSPI as the user and domain are either obtained from the credentials cache
* when using GSS-API or via the currently logged in user's credentials when
* using Windows SSPI.
*
* Parameters:
*
* user [in] - The user name.
*
* Returns TRUE on success; otherwise FALSE.
*/
bool Curl_auth_user_contains_domain(const char *user)
{
bool valid = FALSE;
if(user && *user) {
/* Check we have a domain name or UPN present */
char *p = strpbrk(user, "\\/@");
valid = (p != NULL && p > user && p < user + strlen(user) - 1 ? TRUE :
FALSE);
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
else
/* User and domain are obtained from the GSS-API credentials cache or the
currently logged in user from Windows */
valid = TRUE;
#endif
return valid;
}

215
module/Vendor/CURL/lib/vauth/vauth.h vendored Normal file

@@ -0,0 +1,215 @@
#ifndef HEADER_CURL_VAUTH_H
#define HEADER_CURL_VAUTH_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014 - 2020, Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include <curl/curl.h>
struct Curl_easy;
#if !defined(CURL_DISABLE_CRYPTO_AUTH)
struct digestdata;
#endif
#if defined(USE_NTLM)
struct ntlmdata;
#endif
#if defined(USE_KERBEROS5)
struct kerberos5data;
#endif
#if (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) && defined(USE_SPNEGO)
struct negotiatedata;
#endif
#if defined(USE_WINDOWS_SSPI)
#define GSS_ERROR(status) ((status) & 0x80000000)
#endif
/* This is used to build a SPN string */
#if !defined(USE_WINDOWS_SSPI)
char *Curl_auth_build_spn(const char *service, const char *host,
const char *realm);
#else
TCHAR *Curl_auth_build_spn(const char *service, const char *host,
const char *realm);
#endif
/* This is used to test if the user contains a Windows domain name */
bool Curl_auth_user_contains_domain(const char *user);
/* This is used to generate a base64 encoded PLAIN cleartext message */
CURLcode Curl_auth_create_plain_message(struct Curl_easy *data,
const char *authzid,
const char *authcid,
const char *passwd,
char **outptr, size_t *outlen);
/* This is used to generate a base64 encoded LOGIN cleartext message */
CURLcode Curl_auth_create_login_message(struct Curl_easy *data,
const char *valuep, char **outptr,
size_t *outlen);
/* This is used to generate a base64 encoded EXTERNAL cleartext message */
CURLcode Curl_auth_create_external_message(struct Curl_easy *data,
const char *user, char **outptr,
size_t *outlen);
#if !defined(CURL_DISABLE_CRYPTO_AUTH)
/* This is used to decode a CRAM-MD5 challenge message */
CURLcode Curl_auth_decode_cram_md5_message(const char *chlg64, char **outptr,
size_t *outlen);
/* This is used to generate a CRAM-MD5 response message */
CURLcode Curl_auth_create_cram_md5_message(struct Curl_easy *data,
const char *chlg,
const char *userp,
const char *passwdp,
char **outptr, size_t *outlen);
/* This is used to evaluate if DIGEST is supported */
bool Curl_auth_is_digest_supported(void);
/* This is used to generate a base64 encoded DIGEST-MD5 response message */
CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
const char *chlg64,
const char *userp,
const char *passwdp,
const char *service,
char **outptr, size_t *outlen);
/* This is used to decode a HTTP DIGEST challenge message */
CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
struct digestdata *digest);
/* This is used to generate a HTTP DIGEST response message */
CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const unsigned char *request,
const unsigned char *uri,
struct digestdata *digest,
char **outptr, size_t *outlen);
/* This is used to clean up the digest specific data */
void Curl_auth_digest_cleanup(struct digestdata *digest);
#endif /* !CURL_DISABLE_CRYPTO_AUTH */
#if defined(USE_NTLM)
/* This is used to evaluate if NTLM is supported */
bool Curl_auth_is_ntlm_supported(void);
/* This is used to generate a base64 encoded NTLM type-1 message */
CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
struct ntlmdata *ntlm,
char **outptr,
size_t *outlen);
/* This is used to decode a base64 encoded NTLM type-2 message */
CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
const char *type2msg,
struct ntlmdata *ntlm);
/* This is used to generate a base64 encoded NTLM type-3 message */
CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
char **outptr, size_t *outlen);
/* This is used to clean up the NTLM specific data */
void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm);
#endif /* USE_NTLM */
/* This is used to generate a base64 encoded OAuth 2.0 message */
CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_easy *data,
const char *user,
const char *host,
const long port,
const char *bearer,
char **outptr, size_t *outlen);
/* This is used to generate a base64 encoded XOAuth 2.0 message */
CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_easy *data,
const char *user,
const char *bearer,
char **outptr, size_t *outlen);
#if defined(USE_KERBEROS5)
/* This is used to evaluate if GSSAPI (Kerberos V5) is supported */
bool Curl_auth_is_gssapi_supported(void);
/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token
message */
CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
const bool mutual,
const char *chlg64,
struct kerberos5data *krb5,
char **outptr, size_t *outlen);
/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security
token message */
CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
const char *input,
struct kerberos5data *krb5,
char **outptr,
size_t *outlen);
/* This is used to clean up the GSSAPI specific data */
void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5);
#endif /* USE_KERBEROS5 */
#if defined(USE_SPNEGO)
/* This is used to evaluate if SPNEGO (Negotiate) is supported */
bool Curl_auth_is_spnego_supported(void);
/* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge
message */
CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
const char *user,
const char *passwood,
const char *service,
const char *host,
const char *chlg64,
struct negotiatedata *nego);
/* This is used to generate a base64 encoded SPNEGO (Negotiate) response
message */
CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
struct negotiatedata *nego,
char **outptr, size_t *outlen);
/* This is used to clean up the SPNEGO specifiec data */
void Curl_auth_cleanup_spnego(struct negotiatedata *nego);
#endif /* USE_SPNEGO */
#endif /* HEADER_CURL_VAUTH_H */