From: Francesco Chemolli <5175948+kinkie@users.noreply.github.com> Date: Sun, 9 Mar 2025 09:06:31 +0000 (+0000) Subject: Revert "Remove basic_smb_lm_auth helper" (#2013) X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=256e08b6a8ab8092ebafd86603af56c9c983c2d2;p=thirdparty%2Fsquid.git Revert "Remove basic_smb_lm_auth helper" (#2013) Commit f53a024a40 contained spurious changes. Revert the change --- diff --git a/configure.ac b/configure.ac index c63f92f3e9..5d1568810f 100644 --- a/configure.ac +++ b/configure.ac @@ -2536,6 +2536,7 @@ AC_CONFIG_FILES([ src/auth/basic/RADIUS/Makefile src/auth/basic/SASL/Makefile src/auth/basic/SMB/Makefile + src/auth/basic/SMB_LM/Makefile src/auth/basic/SSPI/Makefile src/auth/digest/Makefile src/auth/digest/eDirectory/Makefile @@ -2547,6 +2548,7 @@ AC_CONFIG_FILES([ src/auth/negotiate/wrapper/Makefile src/auth/ntlm/Makefile src/auth/ntlm/fake/Makefile + src/auth/ntlm/SMB_LM/Makefile src/auth/ntlm/SSPI/Makefile src/base/Makefile src/clients/Makefile diff --git a/doc/release-notes/release-8.sgml.in b/doc/release-notes/release-8.sgml.in index ec0052630c..a8586c7c52 100644 --- a/doc/release-notes/release-8.sgml.in +++ b/doc/release-notes/release-8.sgml.in @@ -36,9 +36,6 @@ The Squid-@SQUID_RELEASE@ change history can be

- --enable-auth-basic= -

Removed SMB_LM helper, in favour of the ntlm_auth - alternative offered by the Samba project. - --enable-external-acl-helpers=

Removed LM_Group helper. The LM protocol is insecure and no longer supported on the market since 2008. diff --git a/src/auth/basic/Makefile.am b/src/auth/basic/Makefile.am index f004532b58..54bdc47588 100644 --- a/src/auth/basic/Makefile.am +++ b/src/auth/basic/Makefile.am @@ -19,6 +19,7 @@ DIST_SUBDIRS = \ RADIUS \ SASL \ SMB \ + SMB_LM \ SSPI SUBDIRS = $(BASIC_AUTH_HELPERS) diff --git a/src/auth/basic/SMB_LM/Makefile.am b/src/auth/basic/SMB_LM/Makefile.am new file mode 100644 index 0000000000..991ccbfe98 --- /dev/null +++ b/src/auth/basic/SMB_LM/Makefile.am @@ -0,0 +1,27 @@ +## Copyright (C) 1996-2023 The Squid Software Foundation and contributors +## +## Squid software is distributed under GPLv2+ license and includes +## contributions from numerous individuals and organizations. +## Please see the COPYING and CONTRIBUTORS files for details. +## + +include $(top_srcdir)/src/Common.am + +libexec_PROGRAMS= basic_smb_lm_auth + +basic_smb_lm_auth_SOURCES = \ + msntauth.cc \ + msntauth.h \ + valid.cc \ + valid.h +basic_smb_lm_auth_LDADD= \ + $(top_builddir)/lib/smblib/libsmblib.la \ + $(top_builddir)/lib/rfcnb/librfcnb.la \ + $(top_builddir)/lib/libmiscencoding.la \ + $(COMPAT_LIB) \ + $(XTRA_LIBS) + +EXTRA_DIST= \ + msntauth-v2.0.lsm \ + README.html \ + required.m4 diff --git a/src/auth/basic/SMB_LM/README.html b/src/auth/basic/SMB_LM/README.html new file mode 100644 index 0000000000..39aac6490f --- /dev/null +++ b/src/auth/basic/SMB_LM/README.html @@ -0,0 +1,151 @@ + + +MSNTAUTH readme + + + + + +

+MSNT Auth v3.0.0
+Squid web proxy NT authentication module
+Modified by the Squid HTTP Proxy team
+Original release by Antonino Iannella, Stellar-X Pty Ltd
+

+ +

Contents

+ + + +

Introduction

+ +

+This is an authentication module for the Squid proxy server +to use an NT domain server. + +

+It originates from the Samba and SMB packages by Andrew Tridgell +and Richard Sharpe. It is sourced from the Pike +authentication module by William Welliver (hwellive@intersil.com), +and the SMB 1.0.1 libraries. +Releases up to version 2.0.3 were created by Antonino Iannella +(antonino@rager.com.au, http://stellarx.tripod.com). +The module is now distributed with Squid, and is maintained by the +Squid proxy team as an Open Source effort. +Msntauth is released under the GNU General Public License. + +

+basic_msnt_auth follows the standard Squid basic authentication helper protocol. +See https://wiki.squid-cache.org/Features/AddonHelpers#basic-scheme for details. +Problems are logged to syslog. + +

+Msntauth works in environments with NT domain controllers on +Windows (TM) NT 4, 2000, and Samba. It only uses the ancient Lanman protocol, +the authenticating systems must be configured to accept it. + +

Installation

+ +

+Msntauth will be compiled when you compile Squid, using +their autoconf system. +Refer to Squid documentation for details. +If the build is suitable, you can skip this section. + +

Configuration

+ +

+As of version 3.0.0, a configuration file is no longer needed. +The specification of the domains and domain controllers to use is +passed as a list of arguments on the command line. + +The syntax is: +

+basic_msnt_auth domain1/domaincontroller1 [domain2/domaincontroller2 ...]
+
+An arbitrary number of domain controllers can be specified, for any number of daomains. +Domain controllers will be attempted in the same order they are configured, until +any of them successfully authenticates the user passed by squid. If all domain +controllers fail to authenticate the user, then access is denied. +Domain controllers can be specified by their NetBios name. + +

+WARNING! this means that a wrong password will be attempted a number of times. +Watch out for domain lock-out policies! + +

Squid.conf changes

+ +

+Refer to Squid documentation for the required changes to squid.conf. +You will need to set the following lines to enable authentication for +your access list - + +

+  acl yourACL proxy_auth REQUIRED
+  http_access allow password
+  http_access allow yourACL
+  http_access deny all
+
+ +

+You will also need to review the following directives. The number of +msntauth children spawned is set with authenticate_children. +The number of children needed is site-dependent, so some +experimentation may be required to find the best number. +There should be no visible delay in performance with Squid once +msntauth is in use. + +Please see http://www.squid-cache.org/Doc/config/auth_param/ or your squid.conf.default +file to check how to configure squid to make use of this helper. + +

Testing

+ +

+I strongly urge that Msntauth is tested prior to being used in a +production environment. It may behave differently on different platforms. +To test it, run it from the command line, and enter username and password +pairs separated by a space. + +

+It should behave in the following way - +

+ - Press ENTER to get an OK or ERR message.
+ - Make sure pressing CTRL-D behaves the same as a carriage return.
+ - Make sure pressing CTRL-C aborts the program.
+ - Test that entering no details does not result in an OK or ERR message.
+ - Test that entering an invalid username and password results in
+   an ERR message. Note that if NT guest user access is allowed on
+   the PDC, an OK message may be returned instead of ERR.
+ - Test that entering an valid username and password results in an OK message.
+   Try usernames which are and aren't in the denied/allowed user files,
+   if they're in use.
+ - Test that entering a guest username and password returns the correct response.
+
+ +

+If the above didn't work as expected, you may need to modify the main() +function in msntauth.c. Inform the Squid maintainers of any problems. + +

+Usernames and passwords are expected to be URL-encoded (see RFC 1738 for details) + +

Support details

+ +

+Refer to the Squid website at http://www.squid-cache.org. +You can submit problems or fixes using the Squid project's Bugzilla database. + + + diff --git a/src/auth/basic/SMB_LM/msntauth-v2.0.lsm b/src/auth/basic/SMB_LM/msntauth-v2.0.lsm new file mode 100644 index 0000000000..91f2445b33 --- /dev/null +++ b/src/auth/basic/SMB_LM/msntauth-v2.0.lsm @@ -0,0 +1,13 @@ +Begin3 +Title: msntauth +Version: 2.0 +Entered-date: 01SEP01 +Description: Squid web proxy NT domain authentication module +Keywords: Squid WWW proxy SMB NT domain authentication module source +Author: antonino@rager.com.au (Antonino Iannella) +Maintained-by: antonino@rager.com.au (Antonino Iannella) +Primary-site: sunsite.unc.edu /pub/Linux/system/network/misc + msntauth-v2.0.tgz +Original-site: http://members.tripod.com/stellarx +Copying-policy: GPL +End diff --git a/src/auth/basic/SMB_LM/msntauth.cc b/src/auth/basic/SMB_LM/msntauth.cc new file mode 100644 index 0000000000..dafb01d42d --- /dev/null +++ b/src/auth/basic/SMB_LM/msntauth.cc @@ -0,0 +1,167 @@ +/* + * Copyright (C) 1996-2023 The Squid Software Foundation and contributors + * + * Squid software is distributed under GPLv2+ license and includes + * contributions from numerous individuals and organizations. + * Please see the COPYING and CONTRIBUTORS files for details. + */ + +/* + * MSNT - Microsoft Windows NT domain squid authenticator module + * Version 2.0 by Stellar-X Pty Ltd, Antonino Iannella + * Sun Sep 2 14:39:53 CST 2001 + * + * Modified to act as a Squid authenticator module. + * Removed all Pike stuff. + * Returns OK for a successful authentication, or ERR upon error. + * + * Uses code from - + * Andrew Tridgell 1997 + * Richard Sharpe 1996 + * Bill Welliver 1999 + * Duane Wessels 2000 (wessels@squid-cache.org) + * + * Released under GNU Public License + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "squid.h" +#include "rfc1738.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include + +#include "auth/basic/SMB_LM/msntauth.h" +#include "auth/basic/SMB_LM/valid.h" + +static char msntauth_version[] = "Msntauth v3.0.0 (C) 2 Sep 2001 Stellar-X Antonino Iannella.\nModified by the Squid HTTP Proxy team 2002-2014"; + +struct domaincontroller { + std::string domain; + std::string server; +}; +typedef std::vector domaincontrollers_t; +domaincontrollers_t domaincontrollers; + +static bool +validate_user(char *username, char *password) +{ + for (domaincontrollers_t::iterator dc = domaincontrollers.begin(); dc != domaincontrollers.end(); ++dc) { + //std::cerr << "testing against " << dc->server << std::endl; + const int rv = Valid_User(username, password, dc->server.c_str(), nullptr, dc->domain.c_str()); + //std::cerr << "check result: " << rv << std::endl; + if (rv == NTV_NO_ERROR) + return true; + } + return false; +} + +static char instructions[] = "Usage instructions: basic_nsnt_auth / [/ ...]"; +static void +display_usage_instructions() +{ + using std::endl; + std::cerr << msntauth_version << endl << instructions << endl << endl; +} + +// arguments: domain/server_name [domain/server_name ...] +int +main(int argc, char **argv) +{ + char username[256]; + char password[256]; + char wstr[256]; + int err = 0; + + openlog("basic_smb_lm_auth", LOG_PID, LOG_USER); + setbuf(stdout, nullptr); + + for (int j = 1; j < argc; ++j) { + std::string arg = argv[j]; + size_t pos=arg.find('/'); + if (arg.find('/',pos+1) != std::string::npos) { + std::cerr << "Error: can't understand domain controller specification '" + << arg << "'. Ignoring" << std::endl; + } + domaincontroller dc; + dc.domain = arg.substr(0,pos); + dc.server = arg.substr(pos+1); + if (dc.domain.length() == 0 || dc.server.length() == 0) { + std::cerr << "Error: invalid domain specification in '" << arg << + "'. Ignoring." << std::endl; + exit(EXIT_FAILURE); + } + domaincontrollers.push_back(dc); + } + if (domaincontrollers.empty()) { + display_usage_instructions(); + std::cerr << "Error: no domain controllers specified" << std::endl; + exit(EXIT_FAILURE); + } + + while (1) { + int n; + /* Read whole line from standard input. Terminate on break. */ + memset(wstr, '\0', sizeof(wstr)); + if (fgets(wstr, 255, stdin) == NULL) + break; + /* ignore this line if we didn't get the end-of-line marker */ + if (NULL == strchr(wstr, '\n')) { + err = 1; + continue; + } + if (err) { + syslog(LOG_WARNING, "oversized message"); + puts("ERR"); + err = 0; + continue; + } + + /* + * extract username and password. + */ + username[0] = '\0'; + password[0] = '\0'; + n = sscanf(wstr, "%s %[^\n]", username, password); + if (2 != n) { + puts("ERR"); + continue; + } + /* Check for invalid or blank entries */ + if ((username[0] == '\0') || (password[0] == '\0')) { + puts("ERR"); + continue; + } + + rfc1738_unescape(username); + rfc1738_unescape(password); + + if (validate_user(username, password)) { + puts("OK"); + } else { + syslog(LOG_INFO, "'%s' login failed", username); + puts("ERR"); + } + err = 0; + } + + return EXIT_SUCCESS; +} + diff --git a/src/auth/basic/SMB_LM/msntauth.h b/src/auth/basic/SMB_LM/msntauth.h new file mode 100644 index 0000000000..b4a496f906 --- /dev/null +++ b/src/auth/basic/SMB_LM/msntauth.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 1996-2023 The Squid Software Foundation and contributors + * + * Squid software is distributed under GPLv2+ license and includes + * contributions from numerous individuals and organizations. + * Please see the COPYING and CONTRIBUTORS files for details. + */ + +#ifndef SQUID_SRC_AUTH_BASIC_SMB_LM_MSNTAUTH_H +#define SQUID_SRC_AUTH_BASIC_SMB_LM_MSNTAUTH_H + +extern int QueryServers(char *, char *); +extern void Check_forallowchange(void); + +#endif /* SQUID_SRC_AUTH_BASIC_SMB_LM_MSNTAUTH_H */ + diff --git a/src/auth/basic/SMB_LM/required.m4 b/src/auth/basic/SMB_LM/required.m4 new file mode 100755 index 0000000000..30caa283b9 --- /dev/null +++ b/src/auth/basic/SMB_LM/required.m4 @@ -0,0 +1,14 @@ +## Copyright (C) 1996-2023 The Squid Software Foundation and contributors +## +## Squid software is distributed under GPLv2+ license and includes +## contributions from numerous individuals and organizations. +## Please see the COPYING and CONTRIBUTORS files for details. +## + +# DONT build this helper on Windows +# DONT build this helper by default +AS_IF([test "x$auto_helpers" != "xyes"],[ + BUILD_HELPER="SMB_LM" + AC_CHECK_HEADERS([w32api/windows.h windows.h],[BUILD_HELPER=""]) + AS_IF([test "x$BUILD_HELPER" = "xSMB_LM"],[require_smblib="yes"]) +]) diff --git a/src/auth/basic/SMB_LM/valid.cc b/src/auth/basic/SMB_LM/valid.cc new file mode 100644 index 0000000000..1aaaea918e --- /dev/null +++ b/src/auth/basic/SMB_LM/valid.cc @@ -0,0 +1,55 @@ +/* + * Copyright (C) 1996-2023 The Squid Software Foundation and contributors + * + * Squid software is distributed under GPLv2+ license and includes + * contributions from numerous individuals and organizations. + * Please see the COPYING and CONTRIBUTORS files for details. + */ + +#include "squid.h" +#include "auth/basic/SMB_LM/valid.h" +#include "smblib/smblib.h" + +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYSLOG_H +#include +#endif + +// BACKUP is unused +int +Valid_User(char *USERNAME, char *PASSWORD, const char *SERVER, char *, const char *DOMAIN) +{ + const char *supportedDialects[] = {"PC NETWORK PROGRAM 1.0", + "MICROSOFT NETWORKS 1.03", + "MICROSOFT NETWORKS 3.0", + "LANMAN1.0", + "LM1.2X002", + "Samba", + "NT LM 0.12", + "NT LANMAN 1.0", + NULL + }; + SMB_Handle_Type con; + + SMB_Init(); + con = SMB_Connect_Server(nullptr, SERVER, DOMAIN); + if (con == NULL) { + return (NTV_SERVER_ERROR); + } + if (SMB_Negotiate(con, supportedDialects) < 0) { /* An error */ + SMB_Discon(con, 0); + return (NTV_PROTOCOL_ERROR); + } + if (SMB_Logon_Server(con, USERNAME, PASSWORD, nullptr, 0) < 0) { + SMB_Discon(con, 0); + return (NTV_LOGON_ERROR); + } + SMB_Discon(con, 0); + return (NTV_NO_ERROR); +} + diff --git a/src/auth/basic/SMB_LM/valid.h b/src/auth/basic/SMB_LM/valid.h new file mode 100644 index 0000000000..bfdb009291 --- /dev/null +++ b/src/auth/basic/SMB_LM/valid.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 1996-2023 The Squid Software Foundation and contributors + * + * Squid software is distributed under GPLv2+ license and includes + * contributions from numerous individuals and organizations. + * Please see the COPYING and CONTRIBUTORS files for details. + */ + +#ifndef SQUID_SRC_AUTH_BASIC_SMB_LM_VALID_H +#define SQUID_SRC_AUTH_BASIC_SMB_LM_VALID_H +/* SMB User verification function */ + +#define NTV_NO_ERROR 0 +#define NTV_SERVER_ERROR 1 +#define NTV_PROTOCOL_ERROR 2 +#define NTV_LOGON_ERROR 3 + +int Valid_User(char *USERNAME, char *PASSWORD, const char *SERVER, char *BACKUP, const char *DOMAIN); + +#endif /* SQUID_SRC_AUTH_BASIC_SMB_LM_VALID_H */ + diff --git a/src/auth/basic/helpers.m4 b/src/auth/basic/helpers.m4 index df1585cfc3..8a13689732 100644 --- a/src/auth/basic/helpers.m4 +++ b/src/auth/basic/helpers.m4 @@ -18,6 +18,7 @@ AS_IF([test "x$enable_auth" != "xno"],[ SQUID_CHECK_HELPER([RADIUS],[auth/basic]) SQUID_CHECK_HELPER([SASL],[auth/basic]) SQUID_CHECK_HELPER([SMB],[auth/basic]) + SQUID_CHECK_HELPER([SMB_LM],[auth/basic]) SQUID_CHECK_HELPER([SSPI],[auth/basic]) SQUID_CHECK_HELPER([fake],[auth/basic]) SQUID_CHECK_HELPER([getpwnam],[auth/basic]) diff --git a/src/auth/ntlm/Makefile.am b/src/auth/ntlm/Makefile.am index 9a11b6125d..b8896fc684 100644 --- a/src/auth/ntlm/Makefile.am +++ b/src/auth/ntlm/Makefile.am @@ -9,6 +9,7 @@ include $(top_srcdir)/src/Common.am DIST_SUBDIRS = \ fake \ + SMB_LM \ SSPI SUBDIRS= $(NTLM_AUTH_HELPERS) EXTRA_DIST= helpers.m4 diff --git a/src/auth/ntlm/SMB_LM/Makefile.am b/src/auth/ntlm/SMB_LM/Makefile.am new file mode 100644 index 0000000000..6933e81424 --- /dev/null +++ b/src/auth/ntlm/SMB_LM/Makefile.am @@ -0,0 +1,23 @@ +## Copyright (C) 1996-2023 The Squid Software Foundation and contributors +## +## Squid software is distributed under GPLv2+ license and includes +## contributions from numerous individuals and organizations. +## Please see the COPYING and CONTRIBUTORS files for details. +## + +include $(top_srcdir)/src/Common.am + +libexec_PROGRAMS = ntlm_smb_lm_auth + +ntlm_smb_lm_auth_SOURCES = ntlm_smb_lm_auth.cc +ntlm_smb_lm_auth_LDADD = \ + $(top_builddir)/lib/smblib/libsmblib.la \ + $(top_builddir)/lib/rfcnb/librfcnb.la \ + $(top_builddir)/lib/ntlmauth/libntlmauth.la \ + $(top_builddir)/lib/libmiscencoding.la \ + $(COMPAT_LIB) \ + $(LIBNETTLE_LIBS) \ + $(CRYPTLIB) \ + $(XTRA_LIBS) + +EXTRA_DIST = required.m4 diff --git a/src/auth/ntlm/SMB_LM/ntlm_smb_lm_auth.cc b/src/auth/ntlm/SMB_LM/ntlm_smb_lm_auth.cc new file mode 100644 index 0000000000..4031e6c260 --- /dev/null +++ b/src/auth/ntlm/SMB_LM/ntlm_smb_lm_auth.cc @@ -0,0 +1,702 @@ +/* + * Copyright (C) 1996-2023 The Squid Software Foundation and contributors + * + * Squid software is distributed under GPLv2+ license and includes + * contributions from numerous individuals and organizations. + * Please see the COPYING and CONTRIBUTORS files for details. + */ + +/* + * (C) 2000 Francesco Chemolli + * Distributed freely under the terms of the GNU General Public License, + * version 2 or later. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#include "squid.h" +#include "base64.h" +#include "compat/debug.h" +#include "helper/protocol_defines.h" +#include "ntlmauth/ntlmauth.h" +#include "ntlmauth/support_bits.cci" +#include "rfcnb/rfcnb.h" +#include "smblib/smblib.h" + +#include +#include +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_GETOPT_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif + +/************* CONFIGURATION ***************/ + +#define DEAD_DC_RETRY_INTERVAL 30 + +/************* END CONFIGURATION ***************/ + +/* A couple of harmless helper macros */ +#define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n"); +#ifdef __GNUC__ +#define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y); +#define SEND3(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y); +#else +/* no gcc, no debugging. varargs macros are a gcc extension */ +#define SEND2 printf +#define SEND3 printf +#endif + +typedef struct _dc dc; +struct _dc { + char *domain; + char *controller; + time_t dead; /* 0 if it's alive, otherwise time of death */ + dc *next; +}; + +/* local functions */ +static void usage(void); +static void process_options(int argc, char *argv[]); +static const char * obtain_challenge(void); +static void manage_request(void); +static const char *make_challenge(char *domain, char *controller); +static char *ntlm_check_auth(ntlm_authenticate * auth, int auth_length); +static void dc_disconnect(void); + +#define ENCODED_PASS_LEN 24 +#define MAX_USERNAME_LEN 255 +#define MAX_DOMAIN_LEN 255 +#define MAX_PASSWD_LEN 31 + +static unsigned char challenge[NTLM_NONCE_LEN]; +static unsigned char lmencoded_empty_pass[ENCODED_PASS_LEN], + ntencoded_empty_pass[ENCODED_PASS_LEN]; +SMB_Handle_Type handle = nullptr; +static NtlmError ntlm_errno; +static char credentials[MAX_USERNAME_LEN+MAX_DOMAIN_LEN+2]; /* we can afford to waste */ +static char my_domain[100], my_domain_controller[100]; +static char errstr[1001]; +#if DEBUG +char error_messages_buffer[NTLM_BLOB_BUFFER_SIZE]; +#endif +char load_balance = 0, protocol_pedantic = 0; +dc *controllers = nullptr; +int numcontrollers = 0; +dc *current_dc; +char smb_error_buffer[1000]; + +/* Disconnects from the DC. A reconnection will be done upon the next request + */ +static void +dc_disconnect() +{ + if (handle != NULL) + SMB_Discon(handle, 0); + handle = nullptr; +} + +/* returns 0 on success, > 0 on failure */ +static int +init_challenge(char *domain, char *domain_controller) +{ + int smberr; + + if (handle != NULL) { + return 0; + } + debug("Connecting to server %s domain %s\n", domain_controller, domain); + handle = SMB_Connect_Server(nullptr, domain_controller, domain); + smberr = SMB_Get_Last_Error(); + SMB_Get_Error_Msg(smberr, errstr, 1000); + + if (handle == NULL) { /* couldn't connect */ + debug("Couldn't connect to SMB Server. Error:%s\n", errstr); + return 1; + } + if (SMB_Negotiate(handle, SMB_Prots) < 0) { /* An error */ + debug("Error negotiating protocol with SMB Server\n"); + SMB_Discon(handle, 0); + handle = nullptr; + return 2; + } + if (handle->Security == 0) { /* share-level security, unusable */ + debug("SMB Server uses share-level security .. we need user security.\n"); + SMB_Discon(handle, 0); + handle = nullptr; + return 3; + } + memcpy(challenge, handle->Encrypt_Key, NTLM_NONCE_LEN); + SMBencrypt((unsigned char *)"",challenge,lmencoded_empty_pass); + SMBNTencrypt((unsigned char *)"",challenge,ntencoded_empty_pass); + return 0; +} + +static const char * +make_challenge(char *domain, char *domain_controller) +{ + /* trying to circumvent some strange problem with pointers in SMBLib */ + /* Ugly as hell, but the lib is going to be dropped... */ + strncpy(my_domain, domain, sizeof(my_domain)-1); + my_domain[sizeof(my_domain)-1] = '\0'; + strncpy(my_domain_controller, domain_controller, sizeof(my_domain_controller)-1); + my_domain_controller[sizeof(my_domain_controller)-1] = '\0'; + + if (init_challenge(my_domain, my_domain_controller) > 0) { + return nullptr; + } + + ntlm_challenge chal; + uint32_t flags = NTLM_REQUEST_NON_NT_SESSION_KEY | + NTLM_CHALLENGE_TARGET_IS_DOMAIN | + NTLM_NEGOTIATE_ALWAYS_SIGN | + NTLM_NEGOTIATE_USE_NTLM | + NTLM_NEGOTIATE_USE_LM | + NTLM_NEGOTIATE_ASCII; + ntlm_make_challenge(&chal, my_domain, my_domain_controller, (char *)challenge, NTLM_NONCE_LEN, flags); + + size_t len = sizeof(chal) - sizeof(chal.payload) + le16toh(chal.target.maxlen); + // for lack of a good NTLM token size limit, allow up to what the helper input can be + // validations later will expect to be limited to that size. + static char b64buf[HELPER_INPUT_BUFFER-10]; /* 10 for other line fields, delimiters and terminator */ + if (base64_encode_len(len) < sizeof(b64buf)-1) { + debug("base64 encoding of the token challenge will exceed %zu bytes", sizeof(b64buf)); + return nullptr; + } + + struct base64_encode_ctx ctx; + base64_encode_init(&ctx); + size_t blen = base64_encode_update(&ctx, b64buf, len, reinterpret_cast(&chal)); + blen += base64_encode_final(&ctx, b64buf+blen); + b64buf[blen] = '\0'; + return b64buf; +} + +/* returns NULL on failure, or a pointer to + * the user's credentials (domain\\username) + * upon success. WARNING. It's pointing to static storage. + * In case of problem sets as side-effect ntlm_errno to one of the + * codes defined in ntlm.h + */ +static char * +ntlm_check_auth(ntlm_authenticate * auth, int auth_length) +{ + char pass[MAX_PASSWD_LEN+1]; + char *domain = credentials; + char *user; + lstring tmp; + + if (handle == NULL) { /*if null we aren't connected, but it shouldn't happen */ + debug("Weird, we've been disconnected\n"); + ntlm_errno = NtlmError::NotConnected; + return nullptr; + } + + /* debug("fetching domain\n"); */ + tmp = ntlm_fetch_string(&(auth->hdr), auth_length, &auth->domain, auth->flags); + if (tmp.str == NULL || tmp.l == 0) { + debug("No domain supplied. Returning no-auth\n"); + ntlm_errno = NtlmError::LoginEror; + return nullptr; + } + if (tmp.l > MAX_DOMAIN_LEN) { + debug("Domain string exceeds %d bytes, rejecting\n", MAX_DOMAIN_LEN); + ntlm_errno = NtlmError::LoginEror; + return nullptr; + } + memcpy(domain, tmp.str, tmp.l); + user = domain + tmp.l; + *user = '\0'; + ++user; + + /* debug("fetching user name\n"); */ + tmp = ntlm_fetch_string(&(auth->hdr), auth_length, &auth->user, auth->flags); + if (tmp.str == NULL || tmp.l == 0) { + debug("No username supplied. Returning no-auth\n"); + ntlm_errno = NtlmError::LoginEror; + return nullptr; + } + if (tmp.l > MAX_USERNAME_LEN) { + debug("Username string exceeds %d bytes, rejecting\n", MAX_USERNAME_LEN); + ntlm_errno = NtlmError::LoginEror; + return nullptr; + } + memcpy(user, tmp.str, tmp.l); + *(user + tmp.l) = '\0'; + + // grab the *response blobs. these are fixed length 24 bytes of binary + const ntlmhdr *packet = &(auth->hdr); + { + const strhdr * str = &auth->lmresponse; + + int16_t len = le16toh(str->len); + int32_t offset = le32toh(str->offset); + + if (len != ENCODED_PASS_LEN || offset + len > auth_length || offset == 0) { + debug("LM response: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", auth_length, len, offset); + ntlm_errno = NtlmError::LoginEror; + return nullptr; + } + tmp.str = (char *)packet + offset; + tmp.l = len; + } + if (tmp.l > MAX_PASSWD_LEN) { + debug("Password string exceeds %d bytes, rejecting\n", MAX_PASSWD_LEN); + ntlm_errno = NtlmError::LoginEror; + return nullptr; + } + + /* Authenticating against the NT response doesn't seem to work... in SMB LM helper. */ + memcpy(pass, tmp.str, tmp.l); + pass[min(MAX_PASSWD_LEN,tmp.l)] = '\0'; + + debug("Empty LM pass detection: user: '%s', ours:'%s', his: '%s' (length: %d)\n", + user,lmencoded_empty_pass,tmp.str,tmp.l); + if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) { + fprintf(stderr,"Empty LM password supplied for user %s\\%s. " + "No-auth\n",domain,user); + ntlm_errno=NtlmError::LoginEror; + return nullptr; + } + + /* still fetch the NT response and check validity against empty password */ + { + const strhdr * str = &auth->ntresponse; + int16_t len = le16toh(str->len); + // NT response field may be absent. that is okay. + if (len != 0) { + int32_t offset = le32toh(str->offset); + + if (len != ENCODED_PASS_LEN || offset + len > auth_length || offset == 0) { + debug("NT response: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", auth_length, len, offset); + ntlm_errno = NtlmError::LoginEror; + return nullptr; + } + tmp.str = (char *)packet + offset; + tmp.l = len; + + debug("Empty NT pass detection: user: '%s', ours:'%s', his: '%s' (length: %d)\n", + user,ntencoded_empty_pass,tmp.str,tmp.l); + if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) { + fprintf(stderr,"ERROR: Empty NT password supplied for user %s\\%s. No-auth\n", domain, user); + ntlm_errno = NtlmError::LoginEror; + return nullptr; + } + } + } + + debug("checking domain: '%s', user: '%s', pass='%s'\n", domain, user, pass); + + const auto rv = SMB_Logon_Server(handle, user, pass, domain, 1); + debug("Login attempt had result %d\n", rv); + + switch (rv) { + case SMBlibE_Success: + ntlm_errno = NtlmError::None; + break; + case SMBlibE_BAD: + ntlm_errno = NtlmError::BlobError; + return nullptr; + case SMBlibE_ProtLow: + case SMBlibE_NoSpace: + case SMBlibE_BadParam: + case SMBlibE_NegNoProt: + case SMBlibE_LowerLayer: + case SMBlibE_SendFailed: + case SMBlibE_RecvFailed: + case SMBlibE_ProtUnknown: + case SMBlibE_NoSuchMsg: + ntlm_errno = NtlmError::ProtocolError; + return nullptr; + case SMBlibE_NotImpl: + case SMBlibE_CallFailed: + case SMBlibE_Remote: + ntlm_errno = NtlmError::ServerError; + return nullptr; + case SMBlibE_GuestOnly: + ntlm_errno = NtlmError::LoginEror; + return nullptr; + default: + ntlm_errno = NtlmError::ServerError; + return nullptr; + } + + *(user - 1) = '\\'; /* hack. Performing, but ugly. */ + + debug("credentials: %s\n", credentials); + return credentials; +} + +static char got_timeout = 0; +/** signal handler to be invoked when the authentication operation + * times out */ +extern "C" void +timeout_during_auth(int) +{ + dc_disconnect(); +} + +/* + * options: + * -b try load-balancing the domain-controllers + * -f fail-over to another DC if DC connection fails. + * DEPRECATED and VERBOSELY IGNORED. This is on by default now. + * -l last-ditch-mode + * domain\controller ... + */ +char *my_program_name = nullptr; + +static void +usage() +{ + fprintf(stderr, + "%s usage:\n%s [-b] [-f] [-d] [-l] domain\\controller [domain\\controller ...]\n" + "-b enables load-balancing among controllers\n" + "-f enables failover among controllers (DEPRECATED and always active)\n" + "-d enables debugging statements if DEBUG was defined at build-time.\n\n" + "You MUST specify at least one Domain Controller.\n" + "You can use either \\ or / as separator between the domain name \n" + "and the controller name\n", + my_program_name, my_program_name); +} + +/* int debug_enabled=0; defined in libcompat */ + +static void +process_options(int argc, char *argv[]) +{ + int opt, j, had_error = 0; + dc *new_dc = nullptr, *last_dc = nullptr; + while (-1 != (opt = getopt(argc, argv, "bfld"))) { + switch (opt) { + case 'b': + load_balance = 1; + break; + case 'f': + fprintf(stderr, + "WARNING. The -f flag is DEPRECATED and always active.\n"); + break; + case 'd': + debug_enabled=1; + break; + default: + fprintf(stderr, "unknown option: -%c. Exiting\n", opt); + usage(); + had_error = 1; + } + } + if (had_error) + exit(EXIT_FAILURE); + /* Okay, now begin filling controllers up */ + /* we can avoid memcpy-ing, and just reuse argv[] */ + for (j = optind; j < argc; ++j) { + char *d, *c; + /* d will not be freed in case of non-error. Since we don't reconfigure, + * it's going to live as long as the process anyways */ + d = static_cast(xmalloc(strlen(argv[j]) + 1)); + strcpy(d, argv[j]); + debug("Adding domain-controller %s\n", d); + if (NULL == (c = strchr(d, '\\')) && NULL == (c = strchr(d, '/'))) { + fprintf(stderr, "Couldn't grok domain-controller %s\n", d); + free(d); + continue; + } + /* more than one delimiter is not allowed */ + if (NULL != strchr(c + 1, '\\') || NULL != strchr(c + 1, '/')) { + fprintf(stderr, "Broken domain-controller %s\n", d); + free(d); + continue; + } + *c= '\0'; + ++c; + new_dc = static_cast(xmalloc(sizeof(dc))); + if (!new_dc) { + fprintf(stderr, "Malloc error while parsing DC options\n"); + free(d); + continue; + } + /* capitalize */ + uc(c); + uc(d); + ++numcontrollers; + new_dc->domain = d; + new_dc->controller = c; + new_dc->dead = 0; + if (controllers == NULL) { /* first controller */ + controllers = new_dc; + last_dc = new_dc; + } else { + last_dc->next = new_dc; /* can't be null */ + last_dc = new_dc; + } + } + if (numcontrollers == 0) { + fprintf(stderr, "You must specify at least one domain-controller!\n"); + usage(); + exit(EXIT_FAILURE); + } + last_dc->next = controllers; /* close the queue, now it's circular */ +} + +/** + * tries connecting to the domain controllers in the "controllers" ring, + * with failover if the adequate option is specified. + */ +static const char * +obtain_challenge() +{ + int j = 0; + const char *ch = nullptr; + for (j = 0; j < numcontrollers; ++j) { + debug("obtain_challenge: selecting %s\\%s (attempt #%d)\n", + current_dc->domain, current_dc->controller, j + 1); + if (current_dc->dead != 0) { + if (time(NULL) - current_dc->dead >= DEAD_DC_RETRY_INTERVAL) { + /* mark helper as retry-worthy if it's so. */ + debug("Reviving DC\n"); + current_dc->dead = 0; + } else { /* skip it */ + debug("Skipping it\n"); + continue; + } + } + /* else branch. Here we KNOW that the DC is fine */ + debug("attempting challenge retrieval\n"); + ch = make_challenge(current_dc->domain, current_dc->controller); + debug("make_challenge retuned %p\n", ch); + if (ch) { + debug("Got it\n"); + return ch; /* All went OK, returning */ + } + /* Huston, we've got a problem. Take this DC out of the loop */ + debug("Marking DC as DEAD\n"); + current_dc->dead = time(NULL); + /* Try with the next */ + debug("moving on to next controller\n"); + current_dc = current_dc->next; + } + /* all DCs failed. */ + return nullptr; +} + +static void +manage_request() +{ + ntlmhdr *fast_header; + char buf[NTLM_BLOB_BUFFER_SIZE]; + char decoded[NTLM_BLOB_BUFFER_SIZE]; + char *ch2, *cred = nullptr; + + if (fgets(buf, NTLM_BLOB_BUFFER_SIZE, stdin) == NULL) { + fprintf(stderr, "fgets() failed! dying..... errno=%d (%s)\n", errno, + strerror(errno)); + exit(EXIT_FAILURE); /* BIIG buffer */ + } + debug("managing request\n"); + ch2 = (char*)memchr(buf, '\n', NTLM_BLOB_BUFFER_SIZE); /* safer against overrun than strchr */ + if (ch2) { + *ch2 = '\0'; /* terminate the string at newline. */ + } + debug("ntlm authenticator. Got '%s' from Squid\n", buf); + + if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */ + /* figure out what we got */ + struct base64_decode_ctx ctx; + base64_decode_init(&ctx); + size_t dstLen = 0; + int decodedLen = 0; + if (!base64_decode_update(&ctx, &dstLen, reinterpret_cast(decoded), strlen(buf)-3, buf+3) || + !base64_decode_final(&ctx)) { + SEND("NA Packet format error, couldn't base64-decode"); + return; + } + decodedLen = dstLen; + + if ((size_t)decodedLen < sizeof(ntlmhdr)) { /* decoding failure, return error */ + SEND("NA Packet format error, truncated packet header."); + return; + } + /* fast-track-decode request type. */ + fast_header = (ntlmhdr *) decoded; + + /* sanity-check: it IS a NTLMSSP packet, isn't it? */ + if (ntlm_validate_packet(fast_header, NTLM_ANY) < 0) { + SEND("NA Broken authentication packet"); + return; + } + switch (le32toh(fast_header->type)) { + case NTLM_NEGOTIATE: + SEND("NA Invalid negotiation request received"); + return; + /* notreached */ + case NTLM_CHALLENGE: + SEND("NA Got a challenge. We refuse to have our authority disputed"); + return; + /* notreached */ + case NTLM_AUTHENTICATE: + /* check against the DC */ + signal(SIGALRM, timeout_during_auth); + alarm(30); + cred = ntlm_check_auth((ntlm_authenticate *) decoded, decodedLen); + alarm(0); + signal(SIGALRM, SIG_DFL); + if (got_timeout != 0) { + fprintf(stderr, "ntlm-auth[%ld]: Timeout during authentication.\n", (long)getpid()); + SEND("BH Timeout during authentication"); + got_timeout = 0; + return; + } + if (cred == NULL) { + int smblib_err, smb_errorclass, smb_errorcode, nb_error; + if (ntlm_errno == NtlmError::LoginEror) { /* hackish */ + SEND("NA Logon Failure"); + return; + } + /* there was an error. We have two errno's to look at. + * libntlmssp's erno is insufficient, we'll have to look at + * the actual SMB library error codes, to actually figure + * out what's happening. The thing has braindamaged interfacess..*/ + smblib_err = SMB_Get_Last_Error(); + smb_errorclass = SMBlib_Error_Class(SMB_Get_Last_SMB_Err()); + smb_errorcode = SMBlib_Error_Code(SMB_Get_Last_SMB_Err()); + nb_error = RFCNB_Get_Last_Error(); + debug("No creds. SMBlib error %d, SMB error class %d, SMB error code %d, NB error %d\n", + smblib_err, smb_errorclass, smb_errorcode, nb_error); + /* Should I use smblib_err? Actually it seems I can do as well + * without it.. */ + if (nb_error != 0) { /* netbios-level error */ + SEND("BH NetBios error!"); + fprintf(stderr, "NetBios error code %d (%s)\n", nb_error, + RFCNB_Error_Strings[abs(nb_error)]); + return; + } + switch (smb_errorclass) { + case SMBC_SUCCESS: + debug("Huh? Got a SMB success code but could check auth.."); + SEND("NA Authentication failed"); + return; + case SMBC_ERRDOS: + /*this is the most important one for errors */ + debug("DOS error\n"); + switch (smb_errorcode) { + /* two categories matter to us: those which could be + * server errors, and those which are auth errors */ + case SMBD_noaccess: /* 5 */ + SEND("NA Access denied"); + return; + case SMBD_badformat: + SEND("NA bad format in authentication packet"); + return; + case SMBD_badaccess: + SEND("NA Bad access request"); + return; + case SMBD_baddata: + SEND("NA Bad Data"); + return; + default: + SEND("BH DOS Error"); + return; + } + case SMBC_ERRSRV: /* server errors */ + debug("Server error"); + switch (smb_errorcode) { + /* mostly same as above */ + case SMBV_badpw: + SEND("NA Bad password"); + return; + case SMBV_access: + SEND("NA Server access error"); + return; + default: + SEND("BH Server Error"); + return; + } + case SMBC_ERRHRD: /* hardware errors don't really matter */ + SEND("BH Domain Controller Hardware error"); + return; + case SMBC_ERRCMD: + SEND("BH Domain Controller Command Error"); + return; + } + SEND("BH unknown internal error."); + return; + } + + lc(cred); /* let's lowercase them for our convenience */ + SEND2("AF %s", cred); + return; + default: + SEND("BH unknown authentication packet type"); + return; + } + /* notreached */ + return; + } + if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */ + dc_disconnect(); + const char *ch = obtain_challenge(); + /* Robert says we can afford to wait forever. I'll trust him on this + * one */ + while (ch == NULL) { + sleep(30); + ch = obtain_challenge(); + } + SEND2("TT %s", ch); + return; + } + SEND("BH Helper detected protocol error"); + return; + /********* END ********/ + +} + +int +main(int argc, char *argv[]) +{ + debug("%s " VERSION " " SQUID_BUILD_INFO " starting up...\n", argv[0]); + + my_program_name = argv[0]; + process_options(argc, argv); + + debug("options processed OK\n"); + + /* initialize FDescs */ + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); + + /* select the first domain controller we're going to use */ + current_dc = controllers; + if (load_balance != 0 && numcontrollers > 1) { + int n; + pid_t pid = getpid(); + n = pid % numcontrollers; + debug("load balancing. Selected controller #%d\n", n); + while (n > 0) { + current_dc = current_dc->next; + --n; + } + } + while (1) { + manage_request(); + } + /* notreached */ + return EXIT_SUCCESS; +} + diff --git a/src/auth/ntlm/SMB_LM/required.m4 b/src/auth/ntlm/SMB_LM/required.m4 new file mode 100755 index 0000000000..a0cf7b5d35 --- /dev/null +++ b/src/auth/ntlm/SMB_LM/required.m4 @@ -0,0 +1,17 @@ +## Copyright (C) 1996-2023 The Squid Software Foundation and contributors +## +## Squid software is distributed under GPLv2+ license and includes +## contributions from numerous individuals and organizations. +## Please see the COPYING and CONTRIBUTORS files for details. +## + +# +# DONT build this helper on Windows +# DONT build this helper by default +# +# XXX: do we really need the mingw check? +AS_IF([test "$squid_host_os" != "mingw" -a "x$auto_helpers" != "xyes"],[ + BUILD_HELPER="SMB_LM" + AC_CHECK_HEADERS([w32api/windows.h windows.h],[BUILD_HELPER=""]) + AS_IF([test "x$BUILD_HELPER" = "xSMB_LM"],[require_smblib="yes"]) +]) diff --git a/src/auth/ntlm/helpers.m4 b/src/auth/ntlm/helpers.m4 index 5c0b931538..f1daa5131c 100644 --- a/src/auth/ntlm/helpers.m4 +++ b/src/auth/ntlm/helpers.m4 @@ -9,6 +9,7 @@ AS_IF([test "x$enable_auth" != "xno"],[ SQUID_HELPER_FEATURE_CHECK([auth_ntlm],[$enable_auth],[auth/ntlm],[ # NP: we only need this list because m4_include() does not accept variables SQUID_CHECK_HELPER([fake],[auth/ntlm]) + SQUID_CHECK_HELPER([SMB_LM],[auth/ntlm]) SQUID_CHECK_HELPER([SSPI],[auth/ntlm]) ]) NTLM_AUTH_HELPERS=$squid_cv_BUILD_HELPERS