+++ /dev/null
-.if !'po4a'hide' .TH ntlm_sspi_auth.exe 8
-.
-.SH NAME
-ntlm_sspi_auth.exe \- Native Windows NTLM/NTLMv2 authenticator for Squid
-.PP
-Version 1.22
-.
-.SH SYNOPSIS
-.if !'po4a'hide' .B ntlm_sspi_auth.exe
-.if !'po4a'hide' .B "[\-dhv] [\-A "
-Group Name
-.if !'po4a'hide' .B "] [\-D "
-Group Name
-.if !'po4a'hide' .B "]"
-.
-.SH DESCRIPTION
-.B ntlm_sspi_auth.exe
-is an installed binary built on Windows systems. It provides native access to the
-Security Service Provider Interface of Windows for authenticating with NTLM / NTLMv2.
-It has automatic support for NTLM NEGOTIATE packets.
-.
-.SH OPTIONS
-.if !'po4a'hide' .TP 12
-.if !'po4a'hide' .B \-A
-Specify a Windows Local Group name allowed to authenticate.
-.
-.if !'po4a'hide' .TP
-.if !'po4a'hide' .B \-d
-Write debug info to stderr.
-.
-.if !'po4a'hide' .TP
-.if !'po4a'hide' .B \-D
-Specify a Windows Local Group name which is to be denied authentication.
-.
-.if !'po4a'hide' .TP
-.if !'po4a'hide' .B \-h
-Display the binary help and command line syntax info using stderr.
-.
-.if !'po4a'hide' .TP
-.if !'po4a'hide' .B \-v
-Enables verbose NTLM packet debugging.
-.
-.SH CONFIGURATION
-.PP
-.B Allowing Users
-.PP
-Users that are allowed to access the web proxy must have the Windows NT
-User Rights "logon from the network".
-.PP
-Optionally the authenticator can verify the NT LOCAL group membership of
-the user against the User Group specified in the Authenticator's command
-line.
-.PP
-This can be accomplished creating a local user group on the NT machine,
-grant the privilege, and adding users to it, it works only with MACHINE
-Local Groups, not Domain Local Groups.
-.PP
-Better group checking is available with external ACL, see
-.B ext_ad_group_acl.exe
-documentation.
-.PP
-.B squid.conf
-typical minimal required changes:
-.if !'po4a'hide' .RS
-.if !'po4a'hide' .B auth_param ntlm program c:/squid/libexec/ntlm_sspi_auth.exe
-.if !'po4a'hide' .B auth_param ntlm children 5
-.if !'po4a'hide' .br
-.if !'po4a'hide' .B acl password proxy_auth REQUIRED
-.if !'po4a'hide' .br
-.if !'po4a'hide' .B http_access allow password
-.if !'po4a'hide' .B http_access deny all
-.if !'po4a'hide' .RE
-.
-.PP
-Refer to Squid documentation for more details.
-.
-.PP
-Internet Explorer has some problems with
-.B ftp://
-URLs when handling internal Squid FTP icons.
-The following
-.B squid.conf
-ACL works around this when placed before the authentication ACL:
-.if !'po4a'hide' .RS
-.if !'po4a'hide' .B acl internal_icons urlpath_regex \-i /squid-internal-static/icons/
-.if !'po4a'hide' .br
-.if !'po4a'hide' .B http_access allow our_networks internal_icons
-.if !'po4a'hide' .RE
-.
-.SH AUTHOR
-This program was written by
-.if !'po4a'hide' .I Guido Serassio <guido.serassio@acmeconsulting.it>
-.PP
-Based on prior work in by
-.if !'po4a'hide' .I Francesco Chemolli <kinkie@squid-cache.org>
-.if !'po4a'hide' .I Robert Collins <lifeless@squid-cache.org>
-.PP
-This manual was written by
-.if !'po4a'hide' .I Guido Serassio <guido.serassio@acmeconsulting.it>
-.if !'po4a'hide' .I Amos Jeffries <amosjeffries@squid-cache.org>
-.
-.SH COPYRIGHT
-.PP
- * Copyright (C) 1996-2025 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.
-.PP
-This program and documentation is copyright to the authors named above.
-.PP
-Distributed under the GNU General Public License (GNU GPL) version 2 or later (GPLv2+).
-.
-.SH QUESTIONS
-Questions on the usage of this program can be sent to the
-.I Squid Users mailing list
-.if !'po4a'hide' <squid-users@lists.squid-cache.org>
-.
-.SH REPORTING BUGS
-Bug reports need to be made in English.
-See https://wiki.squid-cache.org/SquidFaq/BugReporting for details of what you need to include with your bug report.
-.PP
-Report bugs or bug fixes using https://bugs.squid-cache.org/
-.PP
-Report serious security bugs to
-.I Squid Bugs <squid-bugs@lists.squid-cache.org>
-.PP
-Report ideas for new improvements to the
-.I Squid Developers mailing list
-.if !'po4a'hide' <squid-dev@lists.squid-cache.org>
-.
-.SH SEE ALSO
-.if !'po4a'hide' .BR squid "(8), "
-.if !'po4a'hide' .BR GPL "(7), "
-.br
-The Squid FAQ wiki
-.if !'po4a'hide' https://wiki.squid-cache.org/SquidFaq
-.br
-The Squid Configuration Manual
-.if !'po4a'hide' http://www.squid-cache.org/Doc/config/
+++ /dev/null
-/*
- * Copyright (C) 1996-2025 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.
- */
-
-/*
- * ntlm_sspi_auth: helper for NTLM Authentication for Squid Cache
- *
- * (C)2002,2005 Guido Serassio - Acme Consulting S.r.l.
- *
- * Authors:
- * Guido Serassio <guido.serassio@acmeconsulting.it>
- * Acme Consulting S.r.l., Italy <http://www.acmeconsulting.it>
- *
- * With contributions from others mentioned in the change history section
- * below.
- *
- * Based on previous work of Francesco Chemolli and Robert Collins.
- *
- * Dependencies: Windows NT4 SP4 and later.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
- *
- * History:
- *
- * Version 1.22
- * 29-10-2005 Guido Serassio
- * Updated for Negotiate auth support.
- * Version 1.21
- * 21-02-2004 Guido Serassio
- * Removed control of use of NTLM NEGOTIATE packet from
- * command line, now the support is automatic.
- * Version 1.20
- * 30-11-2003 Guido Serassio
- * Added support for NTLM local calls.
- * Added control of use of NTLM NEGOTIATE packet from
- * command line.
- * Updated documentation.
- * Version 1.10
- * 07-09-2003 Guido Serassio
- * Now is true NTLM authenticator.
- * More debug info.
- * Updated documentation.
- * Version 1.0
- * 29-06-2002 Guido Serassio
- * First release.
- *
- *
- */
-
-/************* CONFIGURATION ***************/
-
-#define FAIL_DEBUG 0
-
-/************* END CONFIGURATION ***************/
-
-//typedef unsigned char uchar;
-
-#include "squid.h"
-#include "base64.h"
-#include "helper/protocol_defines.h"
-#include "ntlmauth/ntlmauth.h"
-#include "ntlmauth/support_bits.cci"
-#include "sspi/sspwin32.h"
-#include "util.h"
-
-#include <cctype>
-#include <lm.h>
-#if HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-
-int NTLM_packet_debug_enabled = 0;
-static int have_challenge;
-char * NTAllowedGroup;
-char * NTDisAllowedGroup;
-int UseDisallowedGroup = 0;
-int UseAllowedGroup = 0;
-
-#if FAIL_DEBUG
-int fail_debug_enabled = 0;
-#endif
-
-/* returns 1 on success, 0 on failure */
-static int
-Valid_Group(char *UserName, char *Group)
-{
- int result = FALSE;
- WCHAR wszUserName[UNLEN+1]; // Unicode user name
- WCHAR wszGroup[GNLEN+1]; // Unicode Group
-
- LPLOCALGROUP_USERS_INFO_0 pBuf = nullptr;
- LPLOCALGROUP_USERS_INFO_0 pTmpBuf;
- DWORD dwLevel = 0;
- DWORD dwFlags = LG_INCLUDE_INDIRECT;
- DWORD dwPrefMaxLen = -1;
- DWORD dwEntriesRead = 0;
- DWORD dwTotalEntries = 0;
- NET_API_STATUS nStatus;
- DWORD i;
- DWORD dwTotalCount = 0;
-
- /* Convert ANSI User Name and Group to Unicode */
-
- MultiByteToWideChar(CP_ACP, 0, UserName,
- strlen(UserName) + 1, wszUserName,
- sizeof(wszUserName) / sizeof(wszUserName[0]));
- MultiByteToWideChar(CP_ACP, 0, Group,
- strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
-
- /*
- * Call the NetUserGetLocalGroups function
- * specifying information level 0.
- *
- * The LG_INCLUDE_INDIRECT flag specifies that the
- * function should also return the names of the local
- * groups in which the user is indirectly a member.
- */
- nStatus = NetUserGetLocalGroups(nullptr,
- wszUserName,
- dwLevel,
- dwFlags,
- (LPBYTE *) & pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries);
- /*
- * If the call succeeds,
- */
- if (nStatus == NERR_Success) {
- if ((pTmpBuf = pBuf) != NULL) {
- for (i = 0; i < dwEntriesRead; ++i) {
- if (pTmpBuf == NULL) {
- result = FALSE;
- break;
- }
- if (wcscmp(pTmpBuf->lgrui0_name, wszGroup) == 0) {
- result = TRUE;
- break;
- }
- ++pTmpBuf;
- ++dwTotalCount;
- }
- }
- } else
- result = FALSE;
- /*
- * Free the allocated memory.
- */
- if (pBuf != NULL)
- NetApiBufferFree(pBuf);
- return result;
-}
-
-/*
- * Fills auth with the user's credentials.
- *
- * In case of problem returns one of the
- * codes defined in libntlmauth/ntlmauth.h
- */
-static NtlmError
-ntlm_check_auth(ntlm_authenticate * auth, char *user, char *domain, int auth_length)
-{
- char credentials[DNLEN+UNLEN+2]; /* we can afford to waste */
-
- user[0] = '\0';
- domain[0] = '\0';
- if (!NTLM_LocalCall) {
-
- const auto x = ntlm_unpack_auth(auth, user, domain, auth_length);
-
- if (x != NtlmError::None)
- return x;
-
- if (domain[0] == '\0') {
- debug("No domain supplied. Returning no-auth\n");
- return NtlmError::BadRequest;
- }
- if (user[0] == '\0') {
- debug("No username supplied. Returning no-auth\n");
- return NtlmError::BadRequest;
- }
- debug("checking domain: '%s', user: '%s'\n", domain, user);
-
- } else {
- debug("checking local user\n");
- }
-
- snprintf(credentials, DNLEN+UNLEN+2, "%s\\%s", domain, user);
-
- const auto rv = SSP_ValidateNTLMCredentials(auth, auth_length, credentials);
-
- debug("Login attempt had result %d\n", rv);
-
- if (!rv) { /* failed */
- return NtlmError::SspiError;
- }
-
- if (UseAllowedGroup) {
- if (!Valid_Group(credentials, NTAllowedGroup)) {
- debug("User %s not in allowed Group %s\n", credentials, NTAllowedGroup);
- return NtlmError::BadNtGroup;
- }
- }
- if (UseDisallowedGroup) {
- if (Valid_Group(credentials, NTDisAllowedGroup)) {
- debug("User %s is in denied Group %s\n", credentials, NTDisAllowedGroup);
- return NtlmError::BadNtGroup;
- }
- }
-
- debug("credentials: %s\n", credentials);
- return NtlmError::None;
-}
-
-static void
-helperfail(const char *reason)
-{
-#if FAIL_DEBUG
- fail_debug_enabled =1;
-#endif
- SEND_BH(reason);
-}
-
-/*
- options:
- -d enable debugging.
- -v enable verbose NTLM packet debugging.
- -A can specify a Windows Local Group name allowed to authenticate.
- -D can specify a Windows Local Group name not allowed to authenticate.
- */
-char *my_program_name = nullptr;
-
-static void
-usage()
-{
- fprintf(stderr,
- "Usage: %s [-d] [-v] [-A|D LocalUserGroup] [-h]\n"
- " -d enable debugging.\n"
- " -v enable verbose NTLM packet debugging.\n"
- " -A specify a Windows Local Group name allowed to authenticate\n"
- " -D specify a Windows Local Group name not allowed to authenticate\n"
- " -h this message\n\n",
- my_program_name);
-}
-
-static void
-process_options(int argc, char *argv[])
-{
- int opt, had_error = 0;
-
- opterr =0;
- while (-1 != (opt = getopt(argc, argv, "hdvA:D:"))) {
- switch (opt) {
- case 'A':
- safe_free(NTAllowedGroup);
- NTAllowedGroup=xstrdup(optarg);
- UseAllowedGroup = 1;
- break;
- case 'D':
- safe_free(NTDisAllowedGroup);
- NTDisAllowedGroup=xstrdup(optarg);
- UseDisallowedGroup = 1;
- break;
- case 'd':
- debug_enabled = 1;
- break;
- case 'v':
- debug_enabled = 1;
- NTLM_packet_debug_enabled = 1;
- break;
- case 'h':
- usage();
- exit(EXIT_SUCCESS);
- case '?':
- opt = optopt;
- [[fallthrough]];
- default:
- fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
- usage();
- had_error = 1;
- }
- }
- if (had_error)
- exit(EXIT_FAILURE);
-}
-
-static bool
-token_decode(size_t *decodedLen, uint8_t decoded[], const char *buf)
-{
- struct base64_decode_ctx ctx;
- base64_decode_init(&ctx);
- if (!base64_decode_update(&ctx, decodedLen, decoded, strlen(buf), buf) ||
- !base64_decode_final(&ctx)) {
- SEND_BH("message=\"base64 decode failed\"");
- fprintf(stderr, "ERROR: base64 decoding failed for: '%s'\n", buf);
- return false;
- }
- return true;
-}
-
-static int
-manage_request()
-{
- ntlmhdr *fast_header;
- char buf[HELPER_INPUT_BUFFER];
- uint8_t decoded[HELPER_INPUT_BUFFER];
- size_t decodedLen = 0;
- char helper_command[3];
- int oversized = 0;
- char * ErrorMessage;
- static ntlm_negotiate local_nego;
- char domain[DNLEN+1];
- char user[UNLEN+1];
-
- /* NP: for some reason this helper sometimes needs to accept
- * from clients that send no negotiate packet. */
- if (memcmp(local_nego.hdr.signature, "NTLMSSP", 8) != 0) {
- memset(&local_nego, 0, sizeof(ntlm_negotiate)); /* reset */
- memcpy(local_nego.hdr.signature, "NTLMSSP", 8); /* set the signature */
- local_nego.hdr.type = le32toh(NTLM_NEGOTIATE); /* this is a challenge */
- local_nego.flags = le32toh(NTLM_NEGOTIATE_ALWAYS_SIGN |
- NTLM_NEGOTIATE_USE_NTLM |
- NTLM_NEGOTIATE_USE_LM |
- NTLM_NEGOTIATE_ASCII );
- }
-
- do {
- if (fgets(buf, sizeof(buf), stdin) == NULL)
- return 0;
-
- char *c = static_cast<char*>(memchr(buf, '\n', sizeof(buf)));
- if (c) {
- if (oversized) {
- helperfail("message=\"illegal request received\"");
- fprintf(stderr, "Illegal request received: '%s'\n", buf);
- return 1;
- }
- *c = '\0';
- } else {
- fprintf(stderr, "No newline in '%s'\n", buf);
- oversized = 1;
- continue;
- }
- } while (false);
-
- if ((strlen(buf) > 3) && NTLM_packet_debug_enabled) {
- if (!token_decode(&decodedLen, decoded, buf+3))
- return 1;
- helper_command[0] = buf[0];
- helper_command[1] = buf[1];
- helper_command[2] = '\0';
- debug("Got '%s' from Squid with data:\n", helper_command);
- hex_dump(reinterpret_cast<unsigned char*>(decoded), decodedLen);
- } else
- debug("Got '%s' from Squid\n", buf);
- if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */
- /* figure out what we got */
- if (strlen(buf) > 3) {
- if (!decodedLen /* already decoded*/ && !token_decode(&decodedLen, decoded, buf+3))
- return 1;
- } else {
- debug("Negotiate packet not supplied - self generated\n");
- memcpy(decoded, &local_nego, sizeof(local_nego));
- decodedLen = sizeof(local_nego);
- }
- if ((size_t)decodedLen < sizeof(ntlmhdr)) { /* decoding failure, return error */
- SEND_ERR("message=\"Packet format error\"");
- return 1;
- }
- /* fast-track-decode request type. */
- fast_header = (struct _ntlmhdr *) decoded;
-
- /* sanity-check: it IS a NTLMSSP packet, isn't it? */
- if (ntlm_validate_packet(fast_header, NTLM_ANY) != NtlmError::None) {
- SEND_ERR("message=\"Broken authentication packet\"");
- return 1;
- }
- switch (fast_header->type) {
- case NTLM_NEGOTIATE: {
- /* Obtain challenge against SSPI */
- debug("attempting SSPI challenge retrieval\n");
- char *c = (char *) SSP_MakeChallenge((ntlm_negotiate *) decoded, decodedLen);
- if (c) {
- SEND_TT(c);
- if (NTLM_packet_debug_enabled) {
- if (!token_decode(&decodedLen, decoded, c))
- return 1;
- debug("send 'TT' to squid with data:\n");
- hex_dump(reinterpret_cast<unsigned char*>(decoded), decodedLen);
- if (NTLM_LocalCall) {
- debug("NTLM Local Call detected\n");
- }
- }
- have_challenge = 1;
- } else
- helperfail("message=\"can't obtain challenge\"");
-
- return 1;
- }
- /* notreached */
- case NTLM_CHALLENGE:
- SEND_ERR("message=\"Got a challenge. We refuse to have our authority disputed\"");
- return 1;
- /* notreached */
- case NTLM_AUTHENTICATE:
- SEND_ERR("message=\"Got authentication request instead of negotiate request\"");
- return 1;
- /* notreached */
- default:
- helperfail("message=\"unknown refresh-request packet type\"");
- return 1;
- }
- return 1;
- }
- if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */
- if (!have_challenge) {
- helperfail("message=\"invalid challenge\"");
- return 1;
- }
- /* figure out what we got */
- if (!decodedLen /* already decoded*/ && !token_decode(&decodedLen, decoded, buf+3))
- return 1;
-
- if ((size_t)decodedLen < sizeof(ntlmhdr)) { /* decoding failure, return error */
- SEND_ERR("message=\"Packet format error\"");
- return 1;
- }
- /* fast-track-decode request type. */
- fast_header = (struct _ntlmhdr *) decoded;
-
- /* sanity-check: it IS a NTLMSSP packet, isn't it? */
- if (ntlm_validate_packet(fast_header, NTLM_ANY) != NtlmError::None) {
- SEND_ERR("message=\"Broken authentication packet\"");
- return 1;
- }
- switch (fast_header->type) {
- case NTLM_NEGOTIATE:
- SEND_ERR("message=\"Invalid negotiation request received\"");
- return 1;
- /* notreached */
- case NTLM_CHALLENGE:
- SEND_ERR("message=\"Got a challenge. We refuse to have our authority disputed\"");
- return 1;
- /* notreached */
- case NTLM_AUTHENTICATE: {
- /* check against SSPI */
- const auto err = ntlm_check_auth((ntlm_authenticate *) decoded, user, domain, decodedLen);
- have_challenge = 0;
- if (err != NtlmError::None) {
-#if FAIL_DEBUG
- fail_debug_enabled =1;
-#endif
- switch (err) {
- case NtlmError::None:
- break;
- case NtlmError::BadNtGroup:
- SEND_ERR("message=\"Incorrect Group Membership\"");
- return 1;
- case NtlmError::BadRequest:
- SEND_ERR("message=\"Incorrect Request Format\"");
- return 1;
- case NtlmError::SspiError:
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr,
- GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
- (LPTSTR) &ErrorMessage,
- 0,
- nullptr);
- if (ErrorMessage[strlen(ErrorMessage) - 1] == '\n')
- ErrorMessage[strlen(ErrorMessage) - 1] = '\0';
- if (ErrorMessage[strlen(ErrorMessage) - 1] == '\r')
- ErrorMessage[strlen(ErrorMessage) - 1] = '\0';
- SEND_ERR(ErrorMessage); // TODO update to new syntax
- LocalFree(ErrorMessage);
- return 1;
- default:
- SEND_ERR("message=\"Unknown Error\"");
- return 1;
- }
- }
- /* let's lowercase them for our convenience */
- lc(domain);
- lc(user);
- fprintf(stdout, "OK user=\"%s\\%s\"\n", domain, user);
- return 1;
- }
- default:
- helperfail("message=\"unknown authentication packet type\"");
- return 1;
- }
- return 1;
- } else { /* not an auth-request */
- helperfail("message=\"illegal request received\"");
- fprintf(stderr, "Illegal request received: '%s'\n", buf);
- return 1;
- }
- helperfail("message=\"detected protocol error\"");
- return 1;
- /********* END ********/
-}
-
-int
-main(int argc, char *argv[])
-{
- my_program_name = argv[0];
-
- process_options(argc, argv);
-
- debug("%s " VERSION " " SQUID_BUILD_INFO " starting up...\n", my_program_name);
-
- if (LoadSecurityDll(SSP_NTLM, NTLM_PACKAGE_NAME) == NULL) {
- fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n");
- exit(EXIT_FAILURE);
- }
- debug("SSPI initialized OK\n");
-
- atexit(UnloadSecurityDll);
-
- /* initialize FDescs */
- setbuf(stdout, nullptr);
- setbuf(stderr, nullptr);
-
- while (manage_request()) {
- /* everything is done within manage_request */
- }
- return EXIT_SUCCESS;
-}
-