├── VERSION ├── debian ├── compat ├── docs ├── dirs ├── watch ├── cntlm.default ├── lintian-override ├── changelog ├── prerm ├── postrm ├── postinst ├── control ├── copyright ├── rules └── cntlm.init ├── win ├── resources.rc ├── Cntlm Homepage.url ├── cntlm.ico ├── Inno5 │ ├── ISCC.exe │ ├── Setup.e32 │ ├── islzma.dll │ ├── ISCmplr.dll │ ├── SetupLdr.e32 │ ├── WizModernImage.bmp │ └── WizModernSmallImage.bmp ├── Software Updates.url ├── Support Website.url ├── README.txt └── setup.iss.in ├── .deepsource.toml ├── config ├── socklen_t.c ├── arc4random_buf.c ├── strlcat.c ├── strlcpy.c ├── strdup.c ├── memset_s.c ├── gethostname.c ├── endian.c └── gss.c ├── .github ├── dependabot.yml ├── workflows │ ├── semgrep.yml │ ├── macos_build.yml │ ├── coverity.yml │ ├── flawfinder.yml │ ├── sonarcloud.yml │ ├── c-cpp.yml │ ├── release.yml │ └── codeql-analysis.yml └── copilot-instructions.md ├── sonar-project.properties ├── .gitignore ├── analyze_cntlm_with_ikos.sh ├── appveyor.yml ├── direct.h ├── pages.h ├── rpm ├── cntlm.sysconfig ├── rules ├── cntlm.spec └── cntlm.init ├── forward.h ├── proxy.h ├── acl.h ├── scanner.h ├── ntlm.h ├── config.h ├── sspi.h ├── kerberos.h ├── socket.h ├── COPYRIGHT ├── globals.h ├── http.h ├── configure ├── swap.h ├── auth.h ├── pac.h ├── pages.c ├── doc ├── valgrind.txt └── cntlm.conf ├── auth.c ├── config.c ├── acl.c ├── xcrypt.h ├── pac.c ├── README ├── socket.c ├── utils.h ├── README.md ├── pac_utils_js.h ├── sspi.c ├── scanner.c ├── Makefile ├── kerberos.c └── ntlm.c /VERSION: -------------------------------------------------------------------------------- 1 | 0.94.0 2 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 13 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README 2 | -------------------------------------------------------------------------------- /win/resources.rc: -------------------------------------------------------------------------------- 1 | 1 ICON "win/cntlm.ico" 2 | -------------------------------------------------------------------------------- /debian/dirs: -------------------------------------------------------------------------------- 1 | usr/sbin 2 | usr/share/lintian/overrides 3 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=3 2 | http://sf.net/cntlm/cntlm-([\.\d]+)\.tar\.gz 3 | -------------------------------------------------------------------------------- /win/Cntlm Homepage.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=http://cntlm.sf.net/ 3 | -------------------------------------------------------------------------------- /win/cntlm.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versat/cntlm/HEAD/win/cntlm.ico -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "cxx" 5 | enabled = true -------------------------------------------------------------------------------- /win/Inno5/ISCC.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versat/cntlm/HEAD/win/Inno5/ISCC.exe -------------------------------------------------------------------------------- /win/Inno5/Setup.e32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versat/cntlm/HEAD/win/Inno5/Setup.e32 -------------------------------------------------------------------------------- /win/Inno5/islzma.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versat/cntlm/HEAD/win/Inno5/islzma.dll -------------------------------------------------------------------------------- /win/Inno5/ISCmplr.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versat/cntlm/HEAD/win/Inno5/ISCmplr.dll -------------------------------------------------------------------------------- /win/Inno5/SetupLdr.e32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versat/cntlm/HEAD/win/Inno5/SetupLdr.e32 -------------------------------------------------------------------------------- /win/Software Updates.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=http://sourceforge.net/projects/cntlm/files/ 3 | -------------------------------------------------------------------------------- /debian/cntlm.default: -------------------------------------------------------------------------------- 1 | # Additional options that are passed to the Daemon. 2 | TIMEOUT=1 3 | RUNAS=cntlm 4 | -------------------------------------------------------------------------------- /win/Inno5/WizModernImage.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versat/cntlm/HEAD/win/Inno5/WizModernImage.bmp -------------------------------------------------------------------------------- /win/Support Website.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=http://sourceforge.net/tracker/?group_id=197861 3 | -------------------------------------------------------------------------------- /win/Inno5/WizModernSmallImage.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/versat/cntlm/HEAD/win/Inno5/WizModernSmallImage.bmp -------------------------------------------------------------------------------- /config/socklen_t.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) { 4 | socklen_t len; 5 | return !!sizeof(len); 6 | } 7 | -------------------------------------------------------------------------------- /debian/lintian-override: -------------------------------------------------------------------------------- 1 | # Use a non-standard permission to help protect passwords 2 | cntlm binary: non-standard-file-perm etc/cntlm.conf 0600 != 0644 3 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | cntlm (0.94.0) unstable; urgency=low 2 | 3 | * Ad-hoc source build, not a Debian maintained package! 4 | 5 | -- David Kubicek Fri, 14 Mar 2010 02:16:39 +0100 6 | 7 | -------------------------------------------------------------------------------- /config/arc4random_buf.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) { 4 | int random_number; 5 | 6 | arc4random_buf(&random_number, sizeof(random_number)); 7 | 8 | return 1; 9 | } 10 | -------------------------------------------------------------------------------- /config/strlcat.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BUFFER_SIZE 16 4 | 5 | int main(int argc, char **argv) { 6 | int retval; 7 | char buffer[BUFFER_SIZE] = {0}; 8 | 9 | retval = strlcat(buffer, "hello", BUFFER_SIZE); 10 | 11 | return !!retval; 12 | } 13 | -------------------------------------------------------------------------------- /config/strlcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BUFFER_SIZE 16 4 | 5 | int main(int argc, char **argv) { 6 | int retval; 7 | char buffer[BUFFER_SIZE] = {0}; 8 | 9 | retval = strlcpy(buffer, "hello", BUFFER_SIZE); 10 | 11 | return !!retval; 12 | } 13 | -------------------------------------------------------------------------------- /config/strdup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) { 5 | int retval; 6 | char * pstrdup; 7 | 8 | pstrdup = strdup("hello"); 9 | retval = !strcmp("hello", pstrdup); 10 | free(pstrdup); 11 | 12 | return retval; 13 | } 14 | -------------------------------------------------------------------------------- /config/memset_s.c: -------------------------------------------------------------------------------- 1 | #define __STDC_WANT_LIB_EXT1__ 1 2 | #include 3 | 4 | #define BUFFER_SIZE 16 5 | 6 | int main(int argc, char **argv) { 7 | int retval; 8 | char buffer[BUFFER_SIZE] = {0}; 9 | 10 | retval = memset_s(buffer, BUFFER_SIZE, 'a', BUFFER_SIZE); 11 | 12 | return !retval; 13 | } 14 | -------------------------------------------------------------------------------- /config/gethostname.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) { 6 | char tmp[300]; 7 | 8 | memset(tmp, 0, sizeof(tmp)); 9 | gethostname(tmp, sizeof(tmp)-1); 10 | if (strlen(tmp)) 11 | printf("%s", tmp); 12 | 13 | return !(!strlen(tmp)); 14 | } 15 | -------------------------------------------------------------------------------- /config/endian.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const uint8_t num[] = { 0xEF, 0xBE }; 5 | 6 | /* 7 | * RC: 1 = LE, 0 = BE 8 | */ 9 | int main(int argc, char **argv) { 10 | int rc; 11 | 12 | rc = (*((uint16_t *)num) == 0xBEEF); 13 | printf("%s\n", rc ? "little endian" : "big endian"); 14 | 15 | return rc; 16 | } 17 | -------------------------------------------------------------------------------- /debian/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | #DEBHELPER# 6 | 7 | # Automatically added by dh_installinit 8 | if [ -x "/etc/init.d/cntlm" ]; then 9 | if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then 10 | invoke-rc.d cntlm stop || exit $? 11 | else 12 | /etc/init.d/cntlm stop || exit $? 13 | fi 14 | fi 15 | # End automatically added section 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 2 | # Set update schedule for GitHub Actions 3 | 4 | version: 2 5 | updates: 6 | 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | # Check for updates to GitHub Actions every week 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | paths: 6 | - .github/workflows/semgrep.yml 7 | schedule: 8 | - cron: '0 0 * * 0' 9 | name: Semgrep 10 | jobs: 11 | semgrep: 12 | name: Scan 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v6 16 | - uses: returntocorp/semgrep-action@v1 17 | with: 18 | publishToken: ${{ secrets.SEMGREP_APP_TOKEN }} 19 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # This is the name and version displayed in the SonarCloud UI. 2 | sonar.projectName=cntlm 3 | sonar.projectVersion=1.0 4 | 5 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 6 | sonar.sources=. 7 | 8 | # Excluded files 9 | sonar.exclusions=config/*.c,duktape/* 10 | 11 | # Encoding of the source code. Default is default system encoding 12 | #sonar.sourceEncoding=UTF-8 13 | 14 | sonar.cfamily.threads=1 15 | -------------------------------------------------------------------------------- /.github/workflows/macos_build.yml: -------------------------------------------------------------------------------- 1 | name: MacOS build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v6 16 | - name: Normal build 17 | run: | 18 | ./configure 19 | make 20 | make clean 21 | - name: Verify debug build 22 | run: | 23 | ./configure 24 | make DEBUG=1 25 | make clean 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | cntlm 3 | cntlm*.exe 4 | cntlm*.zip 5 | cntlm*.rpm 6 | cntlm*.deb 7 | cntlm*.tar.gz 8 | cntlm*.tar.bz2 9 | cntlm*.pkg 10 | /configure-stamp 11 | /config/config.h 12 | /config/endian 13 | /config/gethostname 14 | /config/socklen_t 15 | /config/strdup 16 | /config/arc4random_buf 17 | /config/strlcat 18 | /config/strlcpy 19 | /config/memset_s 20 | /config/gss 21 | /config/*.exe 22 | /win/*.exe 23 | /win/*.dll 24 | /win/setup.iss 25 | /win/cntlm_manual.pdf 26 | /win/cntlm.ini 27 | /win/LICENSE.txt 28 | .vscode 29 | .cproject 30 | .project 31 | .settings/ -------------------------------------------------------------------------------- /.github/workflows/coverity.yml: -------------------------------------------------------------------------------- 1 | # https://scan.coverity.com/projects/versat-cntlm 2 | 3 | name: Coverity Scan 4 | 5 | on: 6 | push: 7 | branches: [master] 8 | schedule: 9 | - cron: '0 3 * * 3' 10 | 11 | jobs: 12 | coverity: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - run: sudo apt install -y libkrb5-dev 16 | - uses: actions/checkout@v6 17 | - run: ./configure 18 | - uses: vapier/coverity-scan-action@v1 19 | with: 20 | command: make 21 | project: ${{ github.repository }} 22 | email: ${{ secrets.COVERITY_SCAN_EMAIL }} 23 | token: ${{ secrets.COVERITY_SCAN_TOKEN }} 24 | -------------------------------------------------------------------------------- /analyze_cntlm_with_ikos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to analyze Cntlm with IKOS (see https://github.com/NASA-SW-VnV/ikos) 3 | set -e 4 | set -x 5 | 6 | make clean 7 | printf 'n\nn\nn\nn\n' | ikos-scan ./configure 8 | printf 'n\n' | ikos-scan make 9 | 10 | ikos_options= 11 | # --proc=intra takes too much time (way more than 10 hours on my system) 12 | #ikos_options="${ikos_options} --proc=intra" 13 | ikos_options="${ikos_options} --partitioning=return" 14 | # --domain=gauge-interval-congruence takes too much time (way more than 10 hours on my system) 15 | #ikos_options="${ikos_options} --domain=gauge-interval-congruence" 16 | ikos ${ikos_options} cntlm.bc -o cntlm.db 17 | 18 | -------------------------------------------------------------------------------- /config/gss.c: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef __APPLE__ 3 | #include 4 | #else 5 | #include 6 | #endif 7 | 8 | OM_uint32 (*_gss_display_status)(OM_uint32 *, OM_uint32, int, gss_OID, OM_uint32 *, gss_buffer_t) = NULL; 9 | 10 | int main(int argc, char **argv) { 11 | int retval = 0; 12 | void *handle; 13 | 14 | char* library = 15 | #ifdef __APPLE__ 16 | "/System/Library/Frameworks/GSS.framework/GSS"; 17 | #else 18 | "libgssapi_krb5.so"; 19 | #endif 20 | handle = dlopen(library, RTLD_LAZY); 21 | if (handle) { 22 | _gss_display_status = dlsym(handle, "gss_display_status"); 23 | retval = _gss_display_status != NULL; 24 | dlclose(handle); 25 | } 26 | 27 | return !!retval; 28 | } -------------------------------------------------------------------------------- /debian/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Postrm script for cntlm 4 | # 5 | 6 | set -e 7 | 8 | NAME=cntlm 9 | 10 | if [ "$1" = "purge" ]; then 11 | #if [ -e /usr/share/debconf/confmodule ]; then 12 | # . /usr/share/debconf/confmodule 13 | # db_purge 14 | #fi 15 | 16 | # Remove SysV initscript 17 | #update-rc.d $NAME remove >/dev/null || true 18 | 19 | # Remove user cntlm 20 | home=`getent passwd $NAME | cut -d : -f 6` 21 | rm -f "$home/$NAME.pid" >/dev/null 2>&1 || true 22 | userdel $NAME || true 23 | rmdir "$home" >/dev/null 2>&1 || true 24 | fi 25 | 26 | #DEBHELPER# 27 | 28 | # Automatically added by dh_installinit 29 | if [ "$1" = "purge" ] ; then 30 | update-rc.d cntlm remove >/dev/null || exit $? 31 | fi 32 | # End automatically added section 33 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Postinst script for cntlm 4 | # 5 | 6 | set -e 7 | 8 | NAME=cntlm 9 | HOME=/var/run/cntlm 10 | 11 | # Create cntlm user and its homedir 12 | if ! getent passwd $NAME >/dev/null; then 13 | adduser --system --home $HOME --shell /bin/sh --disabled-password $NAME 14 | fi 15 | 16 | if ! [ -d $HOME ]; then 17 | mkdir -p $HOME 18 | chmod 755 $HOME 19 | chown -h -R $NAME $HOME 20 | fi 21 | 22 | #DEBHELPER# 23 | 24 | # Automatically added by dh_installinit 25 | if [ -x "/etc/init.d/cntlm" ]; then 26 | update-rc.d cntlm defaults >/dev/null 27 | if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then 28 | invoke-rc.d cntlm start || exit $? 29 | else 30 | /etc/init.d/cntlm start || exit $? 31 | fi 32 | fi 33 | # End automatically added section 34 | -------------------------------------------------------------------------------- /win/README.txt: -------------------------------------------------------------------------------- 1 | Cntlm Installation Manual for Windows 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | - Run setup.exe installer 5 | - Edit cntlm.ini 6 | - Start Cntlm 7 | 8 | Visit http://cntlm.sf.net for HOWTO's and configuration tips. 9 | 10 | Starting and stopping 11 | ~~~~~~~~~~~~~~~~~~~~~ 12 | 13 | You can use Cntlm Start Menu shortcuts to start, stop and configure 14 | the application. Cntlm is installed as an auto-start service. 15 | 16 | OR: 17 | Start -> Settings -> Control Panel -> Administrative Tools -> Services 18 | 19 | OR (command line): 20 | net start cntlm 21 | net stop cntlm 22 | 23 | 24 | Uninstalling 25 | ~~~~~~~~~~~~ 26 | Stop Cntlm service, run uninstaller from your Start Menu, or use 27 | native Windows "Add/Remove Programs" Control Panel. 28 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: 3 | - Visual Studio 2022 4 | clone_folder: c:\projects\cntlm 5 | clone_depth: 10 6 | build_script: 7 | - cmd: >- 8 | C:\cygwin64\setup-x86_64.exe -qgnO -l C:\cygwin64\var\cache\setup -R c:\cygwin64 -s http://cygwin.mirror.constant.com -P ghostscript -P dos2unix -P zip 9 | 10 | C:\cygwin64\bin\bash -e -l -c "cd /cygdrive/c/projects/cntlm && make distclean && ./configure && make DEBUG=1" 11 | 12 | C:\cygwin64\bin\bash -e -l -c "cd /cygdrive/c/projects/cntlm && make distclean && ./configure && CFLAGS=-DUNICODE make" 13 | 14 | C:\cygwin64\bin\bash -e -l -c "cd /cygdrive/c/projects/cntlm && make distclean && ./configure && make && make win" 15 | 16 | C:\cygwin64\bin\bash -e -l -c "cd /cygdrive/c/projects/cntlm && ./cntlm -h" 17 | 18 | artifacts: 19 | - path: cntlm-*.exe 20 | - path: cntlm*.zip 21 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: cntlm 2 | Section: net 3 | Priority: optional 4 | Maintainer: David Watson 5 | Build-Depends: debhelper (>= 5), krb5-multidev 6 | Standards-Version: 3.8.0 7 | Vcs-Git: git://planetwatson.co.uk/cntlm 8 | Vcs-Browser: http://projects.planetwatson.co.uk/repositories/show/cntlm 9 | Homepage: http://cntlm.sourceforge.net/ 10 | 11 | Package: cntlm 12 | Architecture: any 13 | Depends: adduser, libgssapi-krb5-2, ${misc:Depends}, ${shlibs:Depends} 14 | Replaces: ntlmaps 15 | Description: Fast NTLM authentication proxy with tunneling 16 | Cntlm is a fast and efficient NTLM proxy, with support for TCP/IP tunneling, 17 | authenticated connection caching, ACLs, proper daemon logging and behaviour 18 | and much more. It has up to ten times faster responses than similar NTLM 19 | proxies, while using by orders or magnitude less RAM and CPU. Manual page 20 | contains detailed information. 21 | -------------------------------------------------------------------------------- /direct.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CNTLM is free software; you can redistribute it and/or modify it under the 3 | * terms of the GNU General Public License as published by the Free Software 4 | * Foundation; either version 2 of the License, or (at your option) any later 5 | * version. 6 | * 7 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | * details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 14 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 15 | * 16 | * Copyright (c) 2007 David Kubicek 17 | * 18 | */ 19 | 20 | #ifndef DIRECT_H 21 | #define DIRECT_H 22 | 23 | #include "utils.h" 24 | 25 | extern int host_connect(const char *hostname, int port); 26 | extern rr_data_t direct_request(void *cdata, rr_data_const_t request); 27 | extern void direct_tunnel(void *thread_data); 28 | 29 | #endif /* DIRECT_H */ 30 | -------------------------------------------------------------------------------- /pages.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CNTLM is free software; you can redistribute it and/or modify it under the 3 | * terms of the GNU General Public License as published by the Free Software 4 | * Foundation; either version 2 of the License, or (at your option) any later 5 | * version. 6 | * 7 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | * details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 14 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 15 | * 16 | * Copyright (c) 2007 David Kubicek 17 | * 18 | */ 19 | 20 | #ifndef PAGES_H 21 | #define PAGES_H 22 | 23 | extern char *gen_407_page(const char *http); 24 | extern char *gen_401_page(const char *http, const char *host, int port); 25 | extern char *gen_denied_page(const char *ip); 26 | extern char *gen_502_page(const char *http, const char *msg); 27 | 28 | #endif /* PAGES_H */ 29 | -------------------------------------------------------------------------------- /rpm/cntlm.sysconfig: -------------------------------------------------------------------------------- 1 | ## Path: Network/Proxy/Cntlm 2 | 3 | ## Type: string 4 | ## Default: /usr/sbin/cntlm 5 | # CNTLM binary location 6 | DAEMON="/usr/sbin/cntlm" 7 | 8 | ## Type: string 9 | ## Default: /usr/sbin/cntlm 10 | # Location of CNTLM's PID file. 11 | # Make sure that you or, if used, -U uid can create/write it 12 | PIDFILE="/var/run/cntlm/cntlmd.pid" 13 | 14 | ## Description: Timeout before forced shutdown 15 | ## Type: integer 16 | ## Default: 1 17 | # How long to wait before forcing cntlm to stop with a second 18 | # signal when active connections are still not finished 19 | TIMEOUT=1 20 | 21 | ## Type: string 22 | ## Default: cntlm 23 | # Name or number of the non-privileged account to run as 24 | RUNAS=cntlm 25 | 26 | ## Type: string 27 | ## Default: "CNTLM Authentication Proxy" 28 | # CNTLM custom service description 29 | DESC="CNTLM Authentication Proxy" 30 | 31 | ## Type: string 32 | ## Default: "" 33 | # List o optional arguments one would specify on the command line. 34 | # See the cntlm man page for list of available arguments 35 | # with their description. 36 | OPTARGS="-U $RUNAS -P $PIDFILE" 37 | -------------------------------------------------------------------------------- /rpm/rules: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Usage: rules [binary|clean] 4 | # 5 | 6 | if [ ! -f VERSION -o ! -f Makefile ]; then 7 | echo "This command must be run from the main source directory!" >&2 8 | exit 1 9 | fi 10 | 11 | RPMS="BUILD RPMS SOURCES SPECS SRPMS tmp" 12 | DIR=`pwd`/tmp 13 | NAME=cntlm-`cat VERSION` 14 | 15 | if [ "$1" = "binary" ]; then 16 | rm -f cntlm*.rpm 2>/dev/null 17 | for i in $RPMS; do mkdir -p $DIR/$i; done # Create new rpm build structure 18 | 19 | make tbz2 20 | mv $NAME.tar.bz2 $DIR/SOURCES 21 | cp rpm/cntlm.* $DIR/SOURCES # Prepare build environment 22 | 23 | rpmbuild \ 24 | --define "_topdir $DIR" \ 25 | --define "_sourcedir %_topdir/SOURCES" \ 26 | --define "_builddir %_topdir/BUILD" \ 27 | --define "_buildrootdir %_topdir/BUILD" \ 28 | --define "_rpmdir %_topdir/RPMS" \ 29 | --define "_specdir %_topdir/SPECS" \ 30 | --define "_srcrpmdir %_topdir/SRPMS" \ 31 | -ba $DIR/SOURCES/cntlm.spec 32 | 33 | cp $DIR/SRPMS/*rpm . 2>/dev/null 34 | cp $DIR/RPMS/*/cntlm*rpm . 2>/dev/null 35 | elif [ "$1" = "clean" ]; then 36 | for i in $RPMS; do rm -rf $DIR/$i; done # Clean the whole mess, keep packages 37 | rmdir $DIR 2>/dev/null || true 38 | fi 39 | -------------------------------------------------------------------------------- /.github/workflows/flawfinder.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: flawfinder 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [ master ] 14 | schedule: 15 | - cron: '31 4 * * 4' 16 | 17 | jobs: 18 | flawfinder: 19 | name: Flawfinder 20 | runs-on: ubuntu-latest 21 | permissions: 22 | actions: read 23 | contents: read 24 | security-events: write 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v6 28 | 29 | - name: flawfinder_scan 30 | uses: david-a-wheeler/flawfinder@c57197cd6061453f10a496f30a732bc1905918d1 31 | with: 32 | arguments: '--sarif ./' 33 | output: 'flawfinder_results.sarif' 34 | 35 | - name: Upload analysis results to GitHub Security tab 36 | uses: github/codeql-action/upload-sarif@v4 37 | with: 38 | sarif_file: ${{github.workspace}}/flawfinder_results.sarif 39 | -------------------------------------------------------------------------------- /forward.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CNTLM is free software; you can redistribute it and/or modify it under the 3 | * terms of the GNU General Public License as published by the Free Software 4 | * Foundation; either version 2 of the License, or (at your option) any later 5 | * version. 6 | * 7 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | * details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 14 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 15 | * 16 | * Copyright (c) 2007 David Kubicek 17 | * 18 | */ 19 | 20 | #ifndef FORWARD_H 21 | #define FORWARD_H 22 | 23 | #include "utils.h" 24 | #include "auth.h" 25 | 26 | extern int prepare_http_connect(int sd, struct auth_s *credentials, const char *thost); 27 | extern rr_data_t forward_request(void *cdata, rr_data_t request); 28 | extern int forward_tunnel(void *thread_data); 29 | extern void magic_auth_detect(const char *url); 30 | 31 | #endif /* FORWARD_H */ 32 | -------------------------------------------------------------------------------- /proxy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Management of parent proxies 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2022 Francesco MDE aka fralken, David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef PROXY_H 23 | #define PROXY_H 24 | 25 | #include "utils.h" 26 | #include "auth.h" 27 | 28 | extern int proxy_connect(struct auth_s *credentials, const char* url, const char* hostname); 29 | extern int proxy_authenticate(int *sd, rr_data_t request, rr_data_t response, struct auth_s *creds); 30 | 31 | extern int parent_add(const char *parent, int port); 32 | extern int parent_available(void); 33 | extern void parent_free(void); 34 | 35 | #endif /* PROXY_H */ 36 | -------------------------------------------------------------------------------- /acl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * These are ACL routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef ACL_H 23 | #define ACL_H 24 | 25 | #include 26 | 27 | #include "utils.h" 28 | 29 | /* 30 | * ACL rule datatypes. 31 | */ 32 | enum acl_t { 33 | ACL_ALLOW = 0, 34 | ACL_DENY 35 | }; 36 | 37 | typedef struct { 38 | unsigned int ip; 39 | int mask; 40 | } network_t; 41 | 42 | extern int acl_add(plist_t *rules, char *spec, enum acl_t acl); 43 | extern enum acl_t acl_check(plist_const_t rules, struct sockaddr *caddr); 44 | 45 | #endif /* ACL_H */ 46 | -------------------------------------------------------------------------------- /scanner.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CNTLM is free software; you can redistribute it and/or modify it under the 3 | * terms of the GNU General Public License as published by the Free Software 4 | * Foundation; either version 2 of the License, or (at your option) any later 5 | * version. 6 | * 7 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | * details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 14 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 15 | * 16 | * Copyright (c) 2007 David Kubicek 17 | * 18 | */ 19 | 20 | #ifndef SCANNER_H 21 | #define SCANNER_H 22 | 23 | #include "utils.h" 24 | #include "auth.h" 25 | 26 | /* 27 | * ISA plugin flags 28 | */ 29 | #define PLUG_NONE 0x0000 30 | #define PLUG_SENDHEAD 0x0001 31 | #define PLUG_SENDDATA 0x0002 32 | #define PLUG_ERROR 0x8000 33 | #define PLUG_ALL 0x7FFF 34 | 35 | /* 36 | * Plugin download sample size 37 | */ 38 | #define SAMPLE 4096 39 | 40 | extern int scanner_hook(rr_data_const_t request, rr_data_t response, struct auth_s *credentials, int cd, int *sd, long maxKBs); 41 | 42 | #endif /* SCANNER_H */ 43 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This package was debianized by David Kubicek on 2 | Fri, 01 Jun 2007 10:46:26 +0200. 3 | 4 | The current Debian maintainer is David Watson 5 | 6 | It was downloaded from 7 | 8 | Copyright: 9 | 10 | Copyright (C) 2007 David Kubicek 11 | 12 | License: 13 | 14 | This program is free software; you can redistribute it and/or modify 15 | it under the terms of the GNU General Public License (verison 2) as published 16 | by the Free Software Foundation; either version 2 of the License. 17 | 18 | This program is distributed in the hope that it will be useful, 19 | but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | GNU General Public License for more details. 22 | 23 | You should have received a copy of the GNU General Public License with 24 | the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; 25 | if not, write to the Free Software Foundation, Inc., 51 Franklin St, 26 | Fifth Floor, Boston, MA 02110-1301, USA. 27 | 28 | On Debian systems, the complete text of the GNU General Public 29 | License, version 2, can be found in /usr/share/common-licenses/GPL-2. 30 | 31 | The Debian packaging is (C) 2007, David Kubicek and 32 | is licensed under the GPL, see `/usr/share/common-licenses/GPL'. 33 | -------------------------------------------------------------------------------- /ntlm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * These are NTLM authentication routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef NTLM_H 23 | #define NTLM_H 24 | 25 | #include "auth.h" 26 | 27 | #define NTLM_BUFSIZE 1024 28 | #define NTLM_CHALLENGE_MIN 40 29 | 30 | extern char *ntlm_hash_lm_password(const char *password); 31 | extern char *ntlm_hash_nt_password(const char *password); 32 | extern char *ntlm2_hash_password(const char *username, const char *domain, const char *password); 33 | extern int ntlm_request(char **dst, struct auth_s *creds); 34 | extern int ntlm_response(char **dst, char *challenge, int challen, struct auth_s *creds); 35 | 36 | #endif /* NTLM_H */ 37 | -------------------------------------------------------------------------------- /.github/workflows/sonarcloud.yml: -------------------------------------------------------------------------------- 1 | name: SonarCloud Scan 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | env: 13 | BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed 14 | steps: 15 | - uses: actions/checkout@v6 16 | with: 17 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 18 | - name: Install Build Wrapper 19 | uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v7.0.0 20 | - name: Install packages required for optional configurations of cntlm 21 | run: sudo apt install -y libkrb5-dev 22 | - name: Run build-wrapper 23 | run: | 24 | ./configure 25 | build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} make 26 | - name: SonarQube Scan 27 | uses: SonarSource/sonarqube-scan-action@v7.0.0 28 | env: 29 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | with: 32 | args: > 33 | -Dsonar.organization=${{ github.repository_owner }} 34 | -Dsonar.projectKey=${{ github.repository_owner }}_cntlm 35 | --define sonar.cfamily.compile-commands=${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json 36 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v6 14 | - name: Install stuff for creating packages 15 | run: sudo apt install -y fakeroot rpm dpkg debhelper 16 | - name: Install packages required for optional configurations of cntlm 17 | run: sudo apt install -y libkrb5-dev 18 | - name: Install further tools (clang-tools for scan-build) 19 | run: sudo apt install -y clang-tools 20 | - name: Normal build (gcc) 21 | run: | 22 | CC=gcc ./configure 23 | make 24 | ./cntlm -h 25 | make distclean 26 | - name: Normal build (clang) 27 | run: | 28 | CC=clang ./configure 29 | make 30 | ./cntlm -h 31 | make distclean 32 | - name: Verify debug build 33 | run: | 34 | ./configure 35 | make DEBUG=1 36 | ./cntlm -h 37 | make distclean 38 | - name: Verify package creation and installation 39 | run: | 40 | ./configure 41 | make rpm 42 | make deb 43 | make clean 44 | sudo make install 45 | sudo make uninstall 46 | make distclean 47 | - name: Build via scan-build (exclude duktape) 48 | run: | 49 | ./configure 50 | make duktape.o 51 | scan-build --force-analyze-debug-code make 52 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * These are very basic config file routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef CONFIG_H 23 | #define CONFIG_H 24 | 25 | #include 26 | 27 | #include "utils.h" 28 | 29 | #define CFG_DEFAULT(cf, opt, var, size) { char *__tmp = NULL; if ((__tmp=config_pop(cf, opt)) && !strlen(var)) { strlcpy(var, __tmp, size); } if (__tmp) free(__tmp); } 30 | 31 | typedef struct config_s *config_t; 32 | struct config_s { 33 | hlist_t options; 34 | }; 35 | 36 | extern config_t config_open(const char *fname); 37 | extern void config_set(config_t cf, char *option, char *value); 38 | extern char *config_pop(config_t cf, const char *option); 39 | extern int config_count(config_t cf); 40 | extern void config_close(config_t cf); 41 | 42 | #endif /* CONFIG_H */ 43 | -------------------------------------------------------------------------------- /sspi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * These are SSPI authentication routines for the NTLM module of CNTLM 3 | * Used only on Win32/64 (Cygwin) 4 | * 5 | * CNTLM is free software; you can redistribute it and/or modify it under the 6 | * terms of the GNU General Public License as published by the Free Software 7 | * Foundation; either version 2 of the License, or (at your option) any later 8 | * version. 9 | * 10 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 11 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 13 | * details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 17 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 18 | * 19 | * Copyright (c) 2013 Denis Galkin aka Evengard, David Kubicek 20 | * 21 | */ 22 | 23 | #ifndef SSPI_H 24 | #define SSPI_H 25 | 26 | #ifdef __CYGWIN__ 27 | 28 | #define SECURITY_WIN32 29 | 30 | #include 31 | #include 32 | 33 | #define TOKEN_BUFSIZE 4096 34 | 35 | struct sspi_handle 36 | { 37 | CredHandle credentials; 38 | CtxtHandle context; 39 | }; 40 | 41 | extern int sspi_enabled(void); 42 | extern int sspi_set(char *mode); 43 | extern int sspi_unset(void); 44 | 45 | extern int sspi_request(char **dst, struct sspi_handle *sspi); 46 | extern int sspi_response(char **dst, char *challenge, int challen, struct sspi_handle *sspi); 47 | 48 | #endif /* __CYGWIN__ */ 49 | 50 | #endif /* SSPI_H */ 51 | -------------------------------------------------------------------------------- /kerberos.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CNTLM is free software; you can redistribute it and/or modify it under the 3 | * terms of the GNU General Public License as published by the Free Software 4 | * Foundation; either version 2 of the License, or (at your option) any later 5 | * version. 6 | * 7 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | * details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 14 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 15 | * 16 | * Copyright (c) 2007 David Kubicek 17 | * 18 | */ 19 | 20 | /* 21 | * kerberos.h 22 | * 23 | * Created on: 25/ago/2010 24 | * Author: luca 25 | */ 26 | 27 | #ifndef KERBEROS_H_ 28 | #define KERBEROS_H_ 29 | 30 | #include "auth.h" 31 | 32 | //used in global auth flag 33 | #define KRB_NO_CREDS 0 34 | #define KRB_CREDENTIAL_AVAILABLE 1 35 | #define KRB_FORCE_USE_KRB 2 36 | 37 | //used while auth 38 | #define KRB_NOT_TRIED 0 39 | #define KRB_OK 1 40 | #define KRB_KO 4 41 | 42 | /** 43 | * acquires a kerberos token for default credential using SPN HTTP@ 44 | */ 45 | int acquire_kerberos_token(const char* hostname, struct auth_s *credentials, char** buf, size_t *bufsize); 46 | 47 | /** 48 | * checks if a default cached credential is cached 49 | */ 50 | int check_credential(void); 51 | 52 | #endif /* KERBEROS_H_ */ 53 | -------------------------------------------------------------------------------- /socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * These are socket routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef SOCKET_H 23 | #define SOCKET_H 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "config/config.h" 30 | #include "utils.h" 31 | 32 | #if config_socklen_t != 1 33 | #define socklen_t uint32_t 34 | #endif 35 | 36 | extern int so_resolv(struct addrinfo **addresses, const char *hostname, const int port); 37 | extern int so_resolv_wildcard(struct addrinfo **addresses, const int port, int gateway); 38 | extern int so_connect(struct addrinfo *adresses); 39 | extern int so_listen(plist_t *list, struct addrinfo *adresses, void *aux); 40 | extern int so_dataready(int fd); 41 | extern int so_closed(int fd); 42 | extern int so_recvln(int fd, char **buf, int *size); 43 | 44 | #endif /* SOCKET_H */ 45 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | CNTLM - Authenticating NTLMv2 Web Proxy for Corporate Environment 2 | Copyright (C) 2007-2012 David Kubicek 3 | 4 | 5 | =================================== 6 | IF YOU USE CNTLM, CONSIDER DONATING 7 | =================================== 8 | That's all I have in the way of supporting any further development, 9 | performing involved analyses of users' network issues, bug fixing, 10 | implementing new features and keeping the AWK.cz VPS and file servers 11 | up and running. 12 | The names of any and all significant benefactors will be immortalized 13 | on Cntlm's home page and in the PDF manual forever. 14 | This was originally a project about freedom, created for the greater 15 | good alone, but my current situation changes many things and I may 16 | even be forced to close-source it (against every fiber of my being). 17 | 18 | 19 | ____________________________________________________________________ 20 | This program is free software; you can redistribute it and/or modify 21 | it under the terms of the GNU General Public License as published by 22 | the Free Software Foundation; either version 2 of the License, or 23 | (at your option) any later version. 24 | 25 | This program is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | GNU General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public License 31 | along with this program; if not, write to the Free Software Foundation, 32 | Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 33 | 34 | If you would like to negotiate alternate licensing terms, you may do 35 | so by contacting: David Kubicek 36 | 37 | 38 | -------------------------------------------------------------------------------- /globals.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CNTLM is free software; you can redistribute it and/or modify it under the 3 | * terms of the GNU General Public License as published by the Free Software 4 | * Foundation; either version 2 of the License, or (at your option) any later 5 | * version. 6 | * 7 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | * details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 14 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 15 | * 16 | * Copyright (c) 2007 David Kubicek 17 | * 18 | */ 19 | 20 | /* 21 | * These are globals, mostly run-time options, defined and setup in main module 22 | * proxy.c 23 | */ 24 | 25 | #ifndef _GLOBALS_H 26 | #define _GLOBALS_H 27 | 28 | #include 29 | 30 | #include "utils.h" 31 | #include "auth.h" 32 | 33 | extern int debug; 34 | 35 | extern struct auth_s *g_creds; /* global NTLM credentials */ 36 | 37 | extern int ntlmbasic; /* forward_request() */ 38 | extern int serialize; 39 | extern int scanner_plugin; 40 | extern long scanner_plugin_maxsize; 41 | 42 | extern plist_t threads_list; 43 | extern pthread_mutex_t threads_mtx; 44 | 45 | extern plist_t connection_list; 46 | extern pthread_mutex_t connection_mtx; 47 | 48 | extern int pac_initialized; 49 | 50 | extern hlist_t header_list; /* forward_request() */ 51 | extern hlist_t users_list; /* socks5_thread() */ 52 | extern plist_t scanner_agent_list; /* scanner_hook() */ 53 | extern plist_t noproxy_list; /* proxy_thread() */ 54 | 55 | #endif /* _GLOBALS_H */ 56 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # 3 | # This file was originally written by Joey Hess and Craig Small. 4 | # As a special exception, when this file is copied by dh-make into a 5 | # dh-make output file, you may use that output file without restriction. 6 | # This special exception was added by Craig Small in version 0.37 of dh-make. 7 | 8 | #export DH_VERBOSE=1 9 | 10 | CFLAGS = -Wall 11 | 12 | ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) 13 | CFLAGS += -O0 14 | else 15 | CFLAGS += -O3 16 | endif 17 | 18 | configure: configure-stamp 19 | configure-stamp: 20 | dh_testdir 21 | # Add here commands to configure the package. 22 | ./configure 23 | touch configure-stamp 24 | 25 | build: build-stamp 26 | 27 | build-stamp: configure-stamp 28 | dh_testdir 29 | 30 | # Add here commands to compile the package. 31 | $(MAKE) PREFIX=/usr 32 | 33 | touch $@ 34 | 35 | clean: 36 | dh_testdir 37 | dh_testroot 38 | rm -f build-stamp configure-stamp 39 | 40 | # Add here commands to clean up after the build process. 41 | [ ! -f Makefile ] || $(MAKE) clean 42 | 43 | dh_clean 44 | 45 | install: build 46 | dh_testdir 47 | dh_testroot 48 | dh_prep 49 | dh_installdirs 50 | 51 | # Add here commands to install the package into debian/cntlm. 52 | $(MAKE) DESTDIR=$(CURDIR)/debian/cntlm PREFIX=/usr install 53 | 54 | # Build architecture-independent files here. 55 | binary-indep: build install 56 | # We have nothing to do by default. 57 | 58 | # Build architecture-dependent files here. 59 | binary-arch: build install 60 | dh_testdir 61 | dh_testroot 62 | dh_installchangelogs 63 | dh_installdocs 64 | dh_install 65 | cp debian/lintian-override debian/cntlm/usr/share/lintian/overrides/cntlm 66 | dh_installdebconf 67 | dh_installinit -n 68 | dh_installman doc/cntlm.1 69 | dh_link 70 | dh_strip 71 | dh_compress 72 | dh_fixperms -Xcntlm.conf 73 | dh_installdeb 74 | dh_shlibdeps 75 | dh_gencontrol 76 | dh_md5sums 77 | dh_builddeb 78 | 79 | binary: binary-indep binary-arch 80 | .PHONY: build clean binary-indep binary-arch binary install configure 81 | -------------------------------------------------------------------------------- /http.h: -------------------------------------------------------------------------------- 1 | /* 2 | * HTTP handling routines and related socket stuff for CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef HTTP_H 23 | #define HTTP_H 24 | 25 | #include 26 | 27 | #include "utils.h" 28 | #include "auth.h" 29 | 30 | /* 31 | * A couple of shortcuts for if statements 32 | */ 33 | #define CONNECT(data) ((data) && (data)->req && !strcasecmp("CONNECT", (data)->method)) 34 | #define HEAD(data) ((data) && (data)->req && !strcasecmp("HEAD", (data)->method)) 35 | #define GET(data) ((data) && (data)->req && !strcasecmp("GET", (data)->method)) 36 | 37 | typedef long long int length_t; 38 | 39 | extern int is_http_header(const char *src); 40 | extern char *get_http_header_name(const char *src); 41 | extern char *get_http_header_value(const char *src); 42 | extern int http_parse_basic(hlist_const_t headers, const char *header, struct auth_s *tcreds); 43 | extern int headers_recv(int fd, rr_data_t data); 44 | extern int headers_send(int fd, rr_data_const_t data); 45 | extern int tunnel(int cd, int sd); 46 | extern length_t http_has_body(rr_data_const_t request, rr_data_const_t response); 47 | extern int http_body_send(int writefd, int readfd, rr_data_const_t request, rr_data_const_t response); 48 | extern int http_body_drop(int fd, rr_data_const_t response); 49 | 50 | #endif /* HTTP_H */ 51 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | version: 11 | runs-on: ubuntu-latest 12 | outputs: 13 | tag: ${{ steps.extract.outputs.tag }} 14 | steps: 15 | - name: Extract tag 16 | id: extract 17 | run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT 18 | 19 | build: 20 | needs: version 21 | runs-on: ${{ matrix.os }} 22 | strategy: 23 | matrix: 24 | include: 25 | - os: ubuntu-latest 26 | command: make deb && make rpm 27 | artifacts: | 28 | cntlm*.rpm 29 | cntlm*.deb 30 | - os: windows-latest 31 | command: make win 32 | artifacts: | 33 | cntlm-*-win64.exe 34 | cntlm-*-win64.zip 35 | - os: macos-latest 36 | command: for A in arm64 x86_64; do ARCH=$A make mac && make clean; done 37 | artifacts: | 38 | cntlm-*-macos-*.pkg 39 | cntlm-*-macos-*.zip 40 | 41 | steps: 42 | - name: Install Cygwin and packages (Windows only) 43 | uses: cygwin/cygwin-install-action@master 44 | with: 45 | platform: x86_64 46 | packages: gcc-core make ghostscript dos2unix zip cygrunsrv 47 | if: ${{ matrix.os == 'windows-latest' }} 48 | 49 | - name: Git config (Windows only) 50 | run: git config --global core.autocrlf input 51 | if: ${{ matrix.os == 'windows-latest' }} 52 | 53 | - uses: actions/checkout@v6 54 | 55 | - name: Install packages for creating packages and for Kerberos support (Ubuntu only) 56 | run: | 57 | sudo apt update 58 | sudo apt install -y fakeroot rpm dpkg debhelper libkrb5-dev 59 | shell: bash 60 | if: ${{ matrix.os == 'ubuntu-latest' }} 61 | 62 | - name: Build artifacts 63 | run: ${{ matrix.command }} 64 | shell: bash 65 | 66 | - name: Create or Update Release 67 | uses: softprops/action-gh-release@v2.5.0 68 | with: 69 | files: ${{ matrix.artifacts }} 70 | tag_name: ${{ needs.version.outputs.tag }} 71 | name: Release ${{ needs.version.outputs.tag }} 72 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Search for XL C/C++, CLANG or GCC, with this priority 4 | # 5 | # In the Makefile check the value of $(CC) (e.g. "ifeq ($(CC),gcc)") 6 | # to define specific compiler configurations 7 | # 8 | #set -x 9 | 10 | if [ -n "$CC" ]; then 11 | # If compiler is specified look for the path 12 | CCPATH=$(which "$CC" 2>&1) 13 | if [ -n "${CCPATH%%/*}" ]; then 14 | echo "Unable to find $CC, will search for a compatible compiler ..." 15 | CC= 16 | fi 17 | fi 18 | 19 | if [ -z "$CC" ]; then 20 | CCS="xlc_r clang gcc" 21 | 22 | # 23 | # Look for supported compilers 24 | # 25 | for c in $CCS; do 26 | if CCPATH=$(which "$c" 2>&1) && [ -z "${CCPATH%%/*}" ] && (echo "#include " | $c -fsyntax-only -x c - > /dev/null 2>&1); then 27 | CC="$c" 28 | break 29 | fi 30 | done 31 | fi 32 | 33 | STAMP=configure-stamp 34 | #[ -f $STAMP ] && exit 0 35 | 36 | # 37 | # Make a link to a proper Makefile.* 38 | # 39 | if [ -z "$CC" ]; then 40 | echo "Unable to find GNU GCC, IBM XL C/C++ or clang. Fix your PATH!" 41 | exit 1 42 | else 43 | echo "Using $CCPATH to compile Cntlm" 44 | echo "$CC" > $STAMP 45 | fi 46 | 47 | CONFIG=config/config.h 48 | TESTS="endian gethostname socklen_t strdup arc4random_buf strlcat strlcpy memset_s gss" 49 | 50 | rm -f $CONFIG 51 | echo "#ifndef CONFIGURE_CONFIG_H" > $CONFIG 52 | echo "#define CONFIGURE_CONFIG_H" >> $CONFIG 53 | echo "" >> $CONFIG 54 | for i in $TESTS; do 55 | printf "Checking %s... " "$i" 56 | printf "#define config_%s " "$i" >> $CONFIG 57 | $CC -std=c99 -D__BSD_VISIBLE -D_ALL_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112 -D_ISOC99_SOURCE -D_REENTRANT -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_DARWIN_C_SOURCE -o config/"$i" config/"$i".c 2> /dev/null 58 | rc=$? 59 | 60 | if [ $rc -ne 0 ]; then # -o -n "$OUT" ]; then 61 | rc=0 62 | RET=no 63 | else 64 | RET=$(./config/"$i") 65 | rc=$? 66 | [ -z "$RET" ] && if [ $rc -eq 0 ]; then RET="no"; else RET="yes"; fi 67 | fi 68 | 69 | echo $rc >> $CONFIG 70 | echo "$RET" 71 | done 72 | echo "" >> $CONFIG 73 | echo "#endif // CONFIGURE_CONFIG_H" >> $CONFIG 74 | 75 | while [ "$1" ] 76 | do 77 | case $1 in 78 | --enable-static) 79 | echo "ENABLE_STATIC" >> $STAMP 80 | ;; 81 | *) 82 | echo "Unknown flag $1" 83 | ;; 84 | esac 85 | shift 86 | done 87 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [master] 14 | schedule: 15 | - cron: '0 7 * * 4' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['cpp'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v6 34 | 35 | # Initializes the CodeQL tools for scanning. 36 | - name: Initialize CodeQL 37 | uses: github/codeql-action/init@v4 38 | with: 39 | languages: ${{ matrix.language }} 40 | # If you wish to specify custom queries, you can do so here or in a config file. 41 | # By default, queries listed here will override any specified in a config file. 42 | # Prefix the list here with "+" to use these queries and those in the config file. 43 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 44 | 45 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 46 | # If this step fails, then you should remove it and run the build manually (see below) 47 | - name: Autobuild 48 | uses: github/codeql-action/autobuild@v4 49 | 50 | # ℹ️ Command-line programs to run using the OS shell. 51 | # 📚 https://git.io/JvXDl 52 | 53 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 54 | # and modify them (or add more) to build your code if your project 55 | # uses a compiled language 56 | 57 | #- run: | 58 | # make bootstrap 59 | # make release 60 | 61 | - name: Perform CodeQL Analysis 62 | uses: github/codeql-action/analyze@v4 63 | -------------------------------------------------------------------------------- /swap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * These are little/big endian routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef SWAP_H 23 | #define SWAP_H 24 | 25 | #include 26 | 27 | #include "config/config.h" 28 | 29 | #define swap16(x) \ 30 | ((uint16_t)( \ 31 | (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ 32 | (((uint16_t)(x) & (uint16_t)0xff00U) >> 8) )) 33 | 34 | #define swap32(x) \ 35 | ((uint32_t)( \ 36 | (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ 37 | (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ 38 | (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ 39 | (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24) )) 40 | 41 | #define swap64(x) \ 42 | ((uint64_t)( \ 43 | (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56) | \ 44 | (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ 45 | (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ 46 | (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ 47 | (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ 48 | (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ 49 | (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ 50 | (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) )) 51 | 52 | #if config_endian == 0 53 | # define U16LE(x) swap16(x) 54 | # define U32LE(x) swap32(x) 55 | # define U64LE(x) swap64(x) 56 | # define U16BE(x) (x) 57 | # define U32BE(x) (x) 58 | # define U64BE(x) (x) 59 | #else 60 | # define U16LE(x) (x) 61 | # define U32LE(x) (x) 62 | # define U64LE(x) (x) 63 | # define U16BE(x) swap16(x) 64 | # define U32BE(x) swap32(x) 65 | # define U64BE(x) swap64(x) 66 | #endif 67 | 68 | #endif /* SWAP_H */ 69 | -------------------------------------------------------------------------------- /auth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Credentials related structures and routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef AUTH_H 23 | #define AUTH_H 24 | 25 | #include 26 | 27 | #include "utils.h" 28 | #ifdef __CYGWIN__ 29 | #include "sspi.h" 30 | #endif 31 | 32 | /* 33 | * Although I always prefer structs with pointer refs, I need direct storage 34 | * here to be able to alloc/free it in one go. It is used in a plist_t which 35 | * frees its items, but not recursively. 36 | */ 37 | struct auth_s { 38 | char user[MINIBUF_SIZE]; ///< username as string 39 | char domain[MINIBUF_SIZE]; ///< domain as string 40 | char workstation[MINIBUF_SIZE]; ///< workstation name as string 41 | char passlm[MINIBUF_SIZE]; ///< password/hash as binary data (no string) 42 | char passnt[MINIBUF_SIZE]; ///< password/hash as binary data (no string) 43 | char passntlm2[MINIBUF_SIZE]; ///< password/hash as binary data (no string) 44 | int hashntlm2; 45 | int hashnt; 46 | int hashlm; 47 | #ifdef __CYGWIN__ 48 | struct sspi_handle sspi; 49 | #endif 50 | #if config_gss == 1 51 | int haskrb; 52 | #endif 53 | uint32_t flags; 54 | }; 55 | 56 | #define auth_strcpy(creds, var, value) \ 57 | if ((creds) && (value)) { \ 58 | strlcpy(((creds)->var), (value), MINIBUF_SIZE); \ 59 | } 60 | 61 | #define auth_memcpy(creds, var, value, len) \ 62 | if ((creds) && (value)) { \ 63 | memcpy(((creds)->var), (value), MIN(len, MINIBUF_SIZE)); \ 64 | } 65 | 66 | extern struct auth_s *copy_auth(struct auth_s *dst, const struct auth_s *src, int fullcopy); 67 | extern struct auth_s *dup_auth(const struct auth_s *creds, int fullcopy); 68 | extern void dump_auth(const struct auth_s *creds); 69 | 70 | #endif /* AUTH_H */ 71 | -------------------------------------------------------------------------------- /win/setup.iss.in: -------------------------------------------------------------------------------- 1 | [Setup] 2 | AppId={{4D753458-961F-45DA-B5E3-7B44D4E368B4} 3 | AppName=Cntlm 4 | AppVerName=Cntlm v$VERSION 5 | AppCopyright=Copyright (C) 2007-2012 David Kubicek 6 | AppContact=cntlm@awk.cz 7 | AppPublisher=David Kubicek 8 | AppPublisherURL=http://cntlm.sf.net/ 9 | LicenseFile=LICENSE.txt 10 | ArchitecturesAllowed=x64 11 | 12 | DefaultDirName={pf}\Cntlm 13 | DefaultGroupName=Cntlm 14 | SetupIconFile=cntlm.ico 15 | UninstallDisplayIcon={app}\cntlm.ico 16 | Uninstallable=yes 17 | OutputBaseFileName=cntlm-$VERSION-win64 18 | OutputDir=.. 19 | 20 | [Files] 21 | Source: "cntlm.exe"; DestDir: "{app}" 22 | Source: "cygrunsrv.exe"; DestDir: "{app}" 23 | Source: "cygwin1.dll"; DestDir: "{app}" 24 | Source: "cntlm.ini"; DestDir: "{app}"; Flags: uninsneveruninstall confirmoverwrite 25 | Source: "cntlm_manual.pdf"; DestDir: "{app}" 26 | Source: "LICENSE.txt"; DestDir: "{app}"; 27 | Source: "README.txt"; DestDir: "{app}"; 28 | Source: "Cntlm Homepage.url"; DestDir: "{app}" 29 | Source: "Software Updates.url"; DestDir: "{app}" 30 | Source: "Support Website.url"; DestDir: "{app}" 31 | 32 | [Run] 33 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Stopping Cntlm service..."; Parameters: " --stop cntlm" 34 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Removing Cntlm service..."; Parameters: " --remove cntlm" 35 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Installing Cntlm service..."; Parameters: "--install cntlm -s KILL -t auto -p ""{app}\cntlm.exe"" -d ""Cntlm Authentication Proxy"" -f ""HTTP Accelerator"" -a -f" 36 | 37 | [Icons] 38 | Name: "{group}\cntlm.ini"; Filename: "{app}\cntlm.ini" 39 | Name: "{group}\Start Cntlm Authentication Proxy"; Filename: "{sys}\net.exe"; Parameters: "start cntlm"; WorkingDir: {app} 40 | Name: "{group}\Stop Cntlm Authentication Proxy"; Filename: "{sys}\net.exe"; Parameters: "stop cntlm"; WorkingDir: {app} 41 | Name: "{group}\Tools\Uninstall Cntlm"; Filename: "{uninstallexe}" 42 | Name: "{group}\Tools\Cntlm Homepage"; Filename: "{app}\Cntlm Homepage.url" 43 | Name: "{group}\Tools\Software Updates"; Filename: "{app}\Software Updates.url" 44 | Name: "{group}\Tools\Support Website"; Filename: "{app}\Support Website.url" 45 | Name: "{group}\Tools\PDF configuration manual"; Filename: "{app}\cntlm_manual.pdf" 46 | 47 | [UninstallRun] 48 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Stopping Cntlm service..."; Parameters: " --stop cntlm" 49 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Removing Cntlm service..."; Parameters: " --remove cntlm" 50 | -------------------------------------------------------------------------------- /debian/cntlm.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | ### BEGIN INIT INFO 4 | # Provides: cntlm 5 | # Required-Start: $syslog $time 6 | # Required-Stop: $syslog $time 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: Authenticating HTTP accelerator for NTLM secured proxies 10 | # Description: Cntlm is meant to be given your proxy address and becomming 11 | # the primary proxy then, listening on a selected local port. 12 | # You point all your proxy-aware programs to it and don't ever 13 | # have to deal with proxy authentication again. 14 | ### END INIT INFO 15 | # 16 | # DAEMON Location of the binary 17 | # PIDFILE Make sure that you or, if used, -U uid can create/write it 18 | # TIMEOUT How long to wait for active connections to finish before 19 | # forcing cntlm to stop with a second signal 20 | # RUNAS Name or number of the non-privileged account to run as 21 | # 22 | 23 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 24 | DAEMON=/usr/sbin/cntlm 25 | NAME=cntlm 26 | DESC="CNTLM Authentication Proxy" 27 | 28 | # Set default values 29 | PIDFILE=/var/run/cntlm/cntlm.pid 30 | TIMEOUT=1 31 | RUNAS=cntlm 32 | 33 | test -x $DAEMON || exit 0 34 | 35 | # Include custom values if available 36 | if [ -f /etc/default/cntlm ] ; then 37 | . /etc/default/cntlm 38 | fi 39 | 40 | DAEMON_OPTS="$DAEMON_OPTS -U $RUNAS -P $PIDFILE" 41 | PIDDIR=`dirname $PIDFILE 2>/dev/null` 42 | 43 | start() { 44 | echo -n "Starting $DESC: " 45 | 46 | if [ -n "$PIDDIR" -a ! -d "$PIDDIR" ]; then 47 | mkdir -p "$PIDDIR" 2>/dev/null 48 | chown "$RUNAS" "$PIDDIR" 2>/dev/null 49 | chmod 755 "$PIDDIR" 2>/dev/null 50 | fi 51 | 52 | start-stop-daemon --oknodo --quiet --start --pidfile $PIDFILE --name $NAME --startas $DAEMON -- $DAEMON_OPTS 2>/dev/null 53 | if [ $? -eq 0 ]; then 54 | echo "$NAME." 55 | else 56 | echo "failed!" 57 | fi 58 | } 59 | 60 | stop() { 61 | echo -n "Stopping $DESC: " 62 | start-stop-daemon --oknodo --quiet --stop --retry -HUP/$TIMEOUT/-KILL/2 --pidfile $PIDFILE --name $NAME 2>/dev/null 63 | if [ $? -eq 0 ]; then 64 | echo "$NAME." 65 | else 66 | echo "failed!" 67 | fi 68 | } 69 | 70 | case "$1" in 71 | start) 72 | start 73 | ;; 74 | stop) 75 | stop 76 | ;; 77 | restart|reload|force-reload) 78 | stop 79 | start 80 | ;; 81 | *) 82 | echo "Usage: $0 {start|stop|restart|reload|force-reload}" >&2 83 | exit 2 84 | ;; 85 | esac 86 | 87 | exit 0 88 | -------------------------------------------------------------------------------- /pac.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Parsing of pac files 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2022 Francesco MDE aka fralken, David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef PAC_H 23 | #define PAC_H 24 | 25 | /// @brief Initializes pac parser. 26 | /// @returns 0 on failure and 1 on success. 27 | /// 28 | /// Initializes Duktape JavaScript engine and does few basic initializations specific 29 | /// to pac. 30 | int pac_init(void); 31 | 32 | /// @brief Parses the given PAC file. 33 | /// @param pacfile PAC file to parse. 34 | /// @returns 0 on failure and 1 on success. 35 | /// 36 | /// Reads the given PAC file and evaluates it in the JavaScript context created 37 | /// by pac_init. 38 | int pac_parse_file(const char *pacfile); // PAC file to parse 39 | 40 | /// @brief Parses the given PAC script string. 41 | /// @param pacstring PAC string to parse. 42 | /// @returns 0 on failure and 1 on success. 43 | /// 44 | /// Evaulates the given PAC script string in the JavaScript context created 45 | /// by pac_init. 46 | int pac_parse_string(const char *pacstring); // PAC string to parse 47 | 48 | /// @brief Finds proxy for the given URL and Host. 49 | /// @param url URL to find proxy for. 50 | /// @param host Host part of the URL. 51 | /// @returns proxy string on sucess and NULL on error. 52 | /// 53 | /// Finds proxy for the given URL and Host. This function should be called only 54 | /// after pac engine has been initialized (using pac_init) and pac 55 | /// script has been parsed (using pac_parse_file or pac_parse_string). 56 | const char *pac_find_proxy(const char *url, // URL to find proxy for 57 | const char *host); // Host part of the URL 58 | 59 | /// @brief Destroys JavaSctipt context. 60 | /// 61 | /// This function should be called once you're done with using pac engine. 62 | void pac_cleanup(void); 63 | 64 | #endif /* PAC_H */ 65 | -------------------------------------------------------------------------------- /pages.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Static HTML page generators for CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #include 23 | 24 | #include "pages.h" 25 | #include "utils.h" 26 | 27 | char *gen_407_page(const char *http) { 28 | char *tmp; 29 | if (http == NULL) 30 | http = "HTTP/1.0"; 31 | tmp = zmalloc(BUFSIZE); 32 | snprintf(tmp, BUFSIZE-1, 33 | "%s 407 Access denied\r\n" 34 | "Proxy-Authenticate: Basic realm=\"Cntlm Proxy\"\r\n" 35 | "Content-Type: text/html\r\n\r\n" 36 | "

407 Access denied

Cntlm requests your credentials for proxy access.

", 37 | http); 38 | return tmp; 39 | } 40 | 41 | char *gen_401_page(const char *http, const char *host, int port) { 42 | char *tmp; 43 | if (http == NULL) 44 | http = "HTTP/1.0"; 45 | tmp = zmalloc(BUFSIZE); 46 | snprintf(tmp, BUFSIZE-1, 47 | "%s 401 Access denied\r\n" 48 | "WWW-Authenticate: Basic realm=\"%s:%d\"\r\n" 49 | "Content-Type: text/html\r\n\r\n" 50 | "

401 Access denied

Cntlm proxy requests your credentials for this URL.

", 51 | http, host, port); 52 | return tmp; 53 | } 54 | 55 | char *gen_denied_page(const char *ip) { 56 | char *tmp; 57 | if (ip == NULL) 58 | ip = "client"; 59 | tmp = zmalloc(BUFSIZE); 60 | snprintf(tmp, BUFSIZE-1, 61 | "HTTP/1.0 407 Access denied\r\n" 62 | "Content-Type: text/html\r\n\r\n" 63 | "

Access denied

Your request has been declined, %s is not allowed to connect.

", 64 | ip); 65 | return tmp; 66 | } 67 | 68 | char *gen_502_page(const char *http, const char *msg) { 69 | char *tmp; 70 | if (http == NULL) 71 | http = "HTTP/1.0"; 72 | if (msg == NULL) 73 | msg = "Proxy error"; 74 | tmp = zmalloc(BUFSIZE); 75 | snprintf(tmp, BUFSIZE-1, 76 | "%s 502 %s\r\n" 77 | "Content-Type: text/html\r\n\r\n" 78 | "

502 %s

Cntlm proxy failed to complete the request.

", 79 | http, msg, msg); 80 | return tmp; 81 | } 82 | -------------------------------------------------------------------------------- /doc/valgrind.txt: -------------------------------------------------------------------------------- 1 | Memory leaks 2 | ~~~~~~~~~~~~ 3 | This is Valgrind report on memory usage after some heavy browsing (60 parallel 4 | connections) and daemon termination. ;) 5 | 6 | ==741== malloc/free: in use at exit: 0 bytes in 0 blocks. 7 | ==741== malloc/free: 2,049 allocs, 2,049 frees, 197,861 bytes allocated. 8 | ==741== 9 | ==741== All heap blocks were freed -- no leaks are possible. 10 | 11 | --741-- memcheck: sanity checks: 6 cheap, 1 expensive 12 | --741-- memcheck: auxmaps: 0 auxmap entries (0k, 0M) in use 13 | --741-- memcheck: auxmaps: 0 searches, 0 comparisons 14 | --741-- memcheck: SMs: n_issued = 29 (464k, 0M) 15 | --741-- memcheck: SMs: n_deissued = 6 (96k, 0M) 16 | --741-- memcheck: SMs: max_noaccess = 65535 (1048560k, 1023M) 17 | --741-- memcheck: SMs: max_undefined = 0 (0k, 0M) 18 | --741-- memcheck: SMs: max_defined = 782 (12512k, 12M) 19 | --741-- memcheck: SMs: max_non_DSM = 28 (448k, 0M) 20 | --741-- memcheck: max sec V bit nodes: 1 (0k, 0M) 21 | --741-- memcheck: set_sec_vbits8 calls: 1 (new: 1, updates: 0) 22 | --741-- memcheck: max shadow mem size: 752k, 0M 23 | --741-- translate: fast SP updates identified: 4,916 ( 90.1%) 24 | --741-- translate: generic_known SP updates identified: 342 ( 6.2%) 25 | --741-- translate: generic_unknown SP updates identified: 194 ( 3.5%) 26 | --741-- tt/tc: 9,734 tt lookups requiring 10,056 probes 27 | --741-- tt/tc: 9,734 fast-cache updates, 4 flushes 28 | --741-- transtab: new 4,484 (100,307 -> 1,609,941; ratio 160:10) [0 scs] 29 | --741-- transtab: dumped 0 (0 -> ??) 30 | --741-- transtab: discarded 133 (2,418 -> ??) 31 | --741-- scheduler: 1,103,546 jumps (bb entries). 32 | --741-- scheduler: 6/39,639 major/minor sched events. 33 | --741-- sanity: 7 cheap, 1 expensive checks. 34 | --741-- exectx: 30,011 lists, 221 contexts (avg 0 per list) 35 | --741-- exectx: 4,115 searches, 3,894 full compares (946 per 1000) 36 | --741-- exectx: 0 cmp2, 60 cmp4, 0 cmpAll 37 | 38 | Memory usage 39 | ~~~~~~~~~~~~ 40 | Heap allocation per function can be seen in memory_consumption_with_reqex.pdf. 41 | It is apparent that in cntlm, regular expression matching accounts for more 42 | that 60% of allocated memory. I don't like that - regex is used for parsing the 43 | option -L at startup and later in header manipulation, once for each request to 44 | parse the first HTTP line. Regex was used there as a convenient and transparent 45 | means to split and partly verify strings, but I'm not willing to pay for it 1.5 46 | times as much memory as the application would need without it. 47 | 48 | Ok, now I have made it optional to use reqex - using NTLM_REGEX. Without this 49 | #define, everything is parsed by plain C memory manipulation. The graph in 50 | memory_consumption_without_reqex.pdf shows significant drop in memory usage, 51 | overall less than a half, actually. Both test were done by an automated test 52 | with 60 connections in parallel and the same sites fully loaded. 53 | 54 | Regexes have been removed for good. No point in keeping around old unnecessary 55 | code. 56 | -------------------------------------------------------------------------------- /auth.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Credentials related structures and routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "auth.h" 27 | 28 | struct auth_s *copy_auth(struct auth_s *dst, const struct auth_s *src, int fullcopy) { 29 | dst->hashntlm2 = src->hashntlm2; 30 | dst->hashnt = src->hashnt; 31 | dst->hashlm = src->hashlm; 32 | dst->flags = src->flags; 33 | #if config_gss == 1 34 | dst->haskrb = src->haskrb; 35 | #endif 36 | 37 | strlcpy(dst->domain, src->domain, MINIBUF_SIZE); 38 | strlcpy(dst->workstation, src->workstation, MINIBUF_SIZE); 39 | 40 | if (fullcopy) { 41 | strlcpy(dst->user, src->user, MINIBUF_SIZE); 42 | memcpy(dst->passntlm2, src->passntlm2, MINIBUF_SIZE); 43 | memcpy(dst->passnt, src->passnt, MINIBUF_SIZE); 44 | memcpy(dst->passlm, src->passlm, MINIBUF_SIZE); 45 | } else { 46 | memset(dst->user, 0, MINIBUF_SIZE); 47 | memset(dst->passntlm2, 0, MINIBUF_SIZE); 48 | memset(dst->passnt, 0, MINIBUF_SIZE); 49 | memset(dst->passlm, 0, MINIBUF_SIZE); 50 | } 51 | 52 | return dst; 53 | } 54 | 55 | struct auth_s *dup_auth(const struct auth_s *creds, int fullcopy) { 56 | struct auth_s *tmp; 57 | 58 | tmp = zmalloc(sizeof(struct auth_s)); 59 | return copy_auth(tmp, creds, fullcopy); 60 | } 61 | 62 | void dump_auth(const struct auth_s *creds) { 63 | char *tmp; 64 | 65 | printf("Credentials structure dump:\n"); 66 | if (creds == NULL) { 67 | printf("Struct is not allocated!\n"); 68 | return; 69 | } 70 | 71 | printf("User: %s\n", creds->user); 72 | printf("Domain: %s\n", creds->domain); 73 | printf("Wks: %s\n", creds->workstation); 74 | printf("HashNTLMv2: %d\n", creds->hashntlm2); 75 | printf("HashNT: %d\n", creds->hashnt); 76 | printf("HashLM: %d\n", creds->hashlm); 77 | printf("Flags: %X\n", creds->flags); 78 | 79 | if (!is_memory_all_zero(creds->passntlm2, ARRAY_SIZE(creds->passntlm2))) { 80 | tmp = printmem(creds->passntlm2, 16, 8); 81 | printf("PassNTLMv2: %s\n", tmp); 82 | free(tmp); 83 | } 84 | 85 | if (!is_memory_all_zero(creds->passnt, ARRAY_SIZE(creds->passnt))) { 86 | tmp = printmem(creds->passnt, 16, 8); 87 | printf("PassNT: %s\n", tmp); 88 | free(tmp); 89 | } 90 | 91 | if (!is_memory_all_zero(creds->passlm, ARRAY_SIZE(creds->passlm))) { 92 | tmp = printmem(creds->passlm, 16, 8); 93 | printf("PassLM: %s\n\n", tmp); 94 | free(tmp); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /doc/cntlm.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Cntlm Authentication Proxy Configuration 3 | # 4 | # NOTE: No URL escape encoding is needed. For example, 123@xyz is 5 | # parsed literally, do NOT use 123%40xyz where %40 is the encoding 6 | # of @. In case the password contains special characters like 7 | # spaces, pound signs, etc, it should be enclosed in double quotes. 8 | # For examples, 123#xyz should be "123#xyz". 9 | # Use 0600 perms if you use plaintext password and see cntlm(1) man 10 | # page for more details. 11 | # 12 | 13 | # Username testuser 14 | # Domain corp-uk 15 | # Password password 16 | # NOTE: Use plaintext password only at your own risk 17 | # Use hashes instead. You can use a "cntlm -M" and "cntlm -H" 18 | # command sequence to get the right config for your environment. 19 | # See cntlm man page 20 | # Example secure config shown below. 21 | # PassLM 1AD35398BE6565DDB5C4EF70C0593492 22 | # PassNT 77B9081511704EE852F94227CF48A793 23 | ### Only for user 'testuser', domain 'corp-uk' 24 | # PassNTLMv2 D5826E9C665C37C80B53397D5C07BBCB 25 | 26 | # Specify the netbios hostname cntlm will send to the parent 27 | # proxies. Normally the value is auto-guessed. 28 | # 29 | # Workstation netbios_hostname 30 | 31 | # List of parent proxies to use. More proxies can be defined 32 | # one per line in format : 33 | # 34 | Proxy 10.0.0.41:8080 35 | Proxy 10.0.0.42:8080 36 | 37 | # List addresses you do not want to pass to parent proxies 38 | # * and ? wildcards can be used 39 | # 40 | NoProxy localhost, 127.0.0.*, 10.*, 192.168.* 41 | 42 | # Use a proxy auto-configuration (PAC) file to determine the parent 43 | # proxy based on the request's target host/URL. 44 | # Specify the PAC file path 45 | #Pac /path/to/proxy.pac 46 | 47 | # Specify the port cntlm will listen on 48 | # You can bind cntlm to specific interface by specifying 49 | # the appropriate IP address also in format : 50 | # Cntlm listens on 127.0.0.1:3128 by default 51 | # 52 | Listen 3128 53 | 54 | # If you wish to use the SOCKS5 proxy feature as well, uncomment 55 | # the following option. It can be used several times 56 | # to have SOCKS5 on more than one port or on different network 57 | # interfaces (specify explicit source address for that). 58 | # 59 | # WARNING: The service accepts all requests, unless you use 60 | # SOCKS5User and make authentication mandatory. SOCKS5User 61 | # can be used repeatedly for a whole bunch of individual accounts. 62 | # 63 | #SOCKS5Proxy 8010 64 | #SOCKS5User dave:password 65 | 66 | # Use -M first to detect the best NTLM settings for your proxy. 67 | # Default is to use the only secure hash, NTLMv2, but it is not 68 | # as available as the older stuff. 69 | # 70 | # This example is the most universal setup known to man, but it 71 | # uses the weakest hash ever. I won't have it's usage on my 72 | # conscience. :) Really, try -M first. 73 | # 74 | #Auth LM 75 | #Flags 0x06820000 76 | 77 | # Enable to allow access from other computers 78 | # 79 | #Gateway yes 80 | 81 | # Useful in Gateway mode to allow/restrict certain IPs 82 | # Specifiy individual IPs or subnets one rule per line. 83 | # 84 | #Allow 127.0.0.1 85 | #Deny 0/0 86 | 87 | # GFI WebMonitor-handling plugin parameters, disabled by default 88 | # 89 | #ISAScannerSize 1024 90 | #ISAScannerAgent Wget/ 91 | #ISAScannerAgent APT-HTTP/ 92 | #ISAScannerAgent Yum/ 93 | 94 | # Headers which should be replaced if present in the request 95 | # 96 | #Header User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98) 97 | 98 | # Tunnels mapping local port to a machine behind the proxy. 99 | # The format is :: 100 | # 101 | #Tunnel 11443:remote.com:443 102 | 103 | # Enable SSPI for Windows clients. 104 | # Only NTLM is supported for now. 105 | # 106 | #SSPI NTLM 107 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * These are very basic config file routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "config.h" 28 | #include "globals.h" 29 | 30 | config_t config_open(const char *fname) { 31 | config_t rc; 32 | FILE *fp; 33 | char *buf; 34 | char section[MINIBUF_SIZE] = "global"; 35 | int i; 36 | int j; 37 | int slen; 38 | 39 | fp = fopen(fname, "r"); 40 | if (!fp) 41 | return NULL; 42 | 43 | buf = zmalloc(BUFSIZE); 44 | rc = (config_t)zmalloc(sizeof(struct config_s)); 45 | rc->options = NULL; 46 | 47 | while (!feof(fp)) { 48 | int quote = 0; 49 | const char *tmp = fgets(buf, BUFSIZE, fp); 50 | if (!tmp) 51 | break; 52 | 53 | const int len = MIN(BUFSIZE, strlen(buf)); 54 | if (!len || feof(fp)) 55 | continue; 56 | 57 | /* 58 | * Find first non-empty character 59 | */ 60 | for (j = 0; j < len && isspace((u_char)buf[j]); ++j) {}; 61 | 62 | /* 63 | * Comment? 64 | */ 65 | if (j >= len || buf[j] == '#' || buf[j] == ';') 66 | continue; 67 | 68 | /* 69 | * Find end of keyword 70 | */ 71 | for (i = j; j < len && isalnum((u_char)buf[j]); ++j) {}; 72 | 73 | /* 74 | * Malformed? 75 | */ 76 | if (j >= len) 77 | continue; 78 | 79 | /* 80 | * Is it a section? 81 | */ 82 | if (buf[j] == '[') { 83 | for (++j; j < len && isspace((u_char)buf[j]); ++j) {}; 84 | for (slen = j; j < len && j-slen < MINIBUF_SIZE-1 && buf[j] != ']' && !isspace((u_char)buf[j]); ++j) {}; 85 | if (j-slen > 0) { 86 | strlcpy(section, buf+slen, j-slen+1); 87 | } 88 | continue; 89 | } 90 | 91 | /* 92 | * It's an OK keyword 93 | */ 94 | char * key = substr(buf, i, j-i); 95 | 96 | /* 97 | * Find next non-empty character 98 | */ 99 | for (; j < len && isspace((u_char)buf[j]); ++j) {}; 100 | if (j >= len || buf[j] == '#' || buf[j] == ';') { 101 | free(key); 102 | continue; 103 | } 104 | 105 | /* 106 | * Is value quoted? 107 | */ 108 | if (buf[j] == '"') { 109 | quote = 1; 110 | for (i = ++j; j < len && buf[i] != '"'; ++i) {}; 111 | if (i >= len) { 112 | free(key); 113 | continue; 114 | } 115 | } else 116 | i = len; 117 | 118 | /* 119 | * Get value as quoted or until EOL/comment 120 | */ 121 | char * value = substr(buf, j, i-j); 122 | if (!quote) { 123 | i = strcspn(value, "#"); 124 | if (i != (int)strlen(value)) 125 | value[i] = 0; 126 | trimr(value); 127 | } 128 | 129 | if (debug) 130 | printf("section: %s, %s = '%s'\n", section, key, value); 131 | rc->options = hlist_add(rc->options, key, value, HLIST_NOALLOC, HLIST_NOALLOC); 132 | } 133 | 134 | free(buf); 135 | fclose(fp); 136 | 137 | return rc; 138 | } 139 | 140 | void config_set(config_t cf, char *option, char *value) { 141 | cf->options = hlist_mod(cf->options, option, value, 1); 142 | } 143 | 144 | char *config_pop(config_t cf, const char *option) { 145 | char *tmp; 146 | 147 | tmp = hlist_get(cf->options, option); 148 | if (tmp) { 149 | tmp = strdup(tmp); 150 | cf->options = hlist_del(cf->options, option); 151 | } 152 | 153 | return tmp; 154 | } 155 | 156 | int config_count(config_t cf) { 157 | return hlist_count(cf->options); 158 | } 159 | 160 | void config_close(config_t cf) { 161 | if (cf == NULL) 162 | return; 163 | 164 | cf->options = hlist_free(cf->options); 165 | free(cf); 166 | } 167 | -------------------------------------------------------------------------------- /acl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * These are ACL routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "acl.h" 28 | #include "socket.h" 29 | #include "swap.h" 30 | 31 | /* 32 | * TODO: retest ACLs on big-endian 33 | */ 34 | 35 | /* 36 | * Add the rule spec to the ACL list. 37 | */ 38 | int acl_add(plist_t *rules, char *spec, enum acl_t acl) { 39 | struct addrinfo *addresses = NULL; 40 | struct sockaddr_in *naddr = NULL; 41 | network_t *aux; 42 | int mask = 32; 43 | size_t i; 44 | char *tmp; 45 | 46 | if (rules == NULL) 47 | return 0; 48 | 49 | spec = strdup(spec); 50 | aux = (network_t *)zmalloc(sizeof(network_t)); 51 | i = strcspn(spec, "/"); 52 | if (i < strlen(spec)) { 53 | spec[i] = 0; 54 | mask = (int)strtol(spec+i+1, &tmp, 10); 55 | if (mask < 0 || mask > 32 || spec[i+1] == 0 || *tmp != 0) { 56 | syslog(LOG_ERR, "ACL netmask for %s is invalid\n", spec); 57 | free(aux); 58 | free(spec); 59 | return 0; 60 | } 61 | } 62 | 63 | if (!strcmp("*", spec)) { 64 | aux->ip = 0; 65 | mask = 0; 66 | } else if (!strcmp("0", spec)) { 67 | aux->ip = 0; 68 | } else if (!so_resolv(&addresses, spec, 0)) { 69 | syslog(LOG_ERR, "ACL source address %s is invalid\n", spec); 70 | free(aux); 71 | free(spec); 72 | return 0; 73 | } 74 | 75 | if (addresses != NULL) { 76 | // TODO only ipv4 client addresses are supported for now 77 | for (struct addrinfo *p = addresses; p != NULL; p = p->ai_next) { 78 | if (p->ai_family == AF_INET) { 79 | naddr = (struct sockaddr_in*)p->ai_addr; 80 | break; 81 | } 82 | } 83 | 84 | if (naddr == NULL) { 85 | syslog(LOG_ERR, "ACL only ipv4 source addresses are supported (%s)\n", spec); 86 | free(aux); 87 | free(spec); 88 | freeaddrinfo(addresses); 89 | return 0; 90 | } 91 | 92 | aux->ip = naddr->sin_addr.s_addr; 93 | } 94 | 95 | aux->mask = mask; 96 | mask = swap32(~(((uint64_t)1 << (32-mask)) - 1)); 97 | if ((aux->ip & mask) != aux->ip) 98 | syslog(LOG_WARNING, "Subnet definition might be incorrect: %s/%d\n", naddr ? inet_ntoa(naddr->sin_addr) : spec, aux->mask); 99 | 100 | syslog(LOG_INFO, "New ACL rule: %s %s/%d\n", (acl == ACL_ALLOW ? "allow" : "deny"), naddr ? inet_ntoa(naddr->sin_addr) : spec, aux->mask); 101 | *rules = plist_add(*rules, acl, (char *)aux); 102 | 103 | free(spec); 104 | freeaddrinfo(addresses); 105 | return 1; 106 | } 107 | 108 | /* 109 | * Takes client IP address (network order) and walks the 110 | * ACL rules until a match is found, returning ACL_ALLOW 111 | * or ACL_DENY accordingly. If no rule matches, connection 112 | * is allowed (such is the case with no ACLs). 113 | * 114 | * Proper policy should always end with a default rule, 115 | * targeting either "*" or "0/0" to explicitly express 116 | * one's intentions. 117 | */ 118 | enum acl_t acl_check(plist_const_t rules, struct sockaddr *caddr) { 119 | // TODO only ipv4 client addresses are supported for now 120 | if (rules && caddr->sa_family == AF_INET) { 121 | const struct sockaddr_in* naddr = (struct sockaddr_in*)caddr; 122 | while (rules) { 123 | const network_t * const aux = (network_t *)rules->aux; 124 | const unsigned int mask = swap32(~(((uint64_t)1 << (32-aux->mask)) - 1)); 125 | 126 | if ((naddr->sin_addr.s_addr & mask) == (aux->ip & mask)) 127 | return (enum acl_t)rules->key; 128 | 129 | rules = rules->next; 130 | } 131 | } 132 | 133 | return ACL_ALLOW; 134 | } 135 | -------------------------------------------------------------------------------- /xcrypt.h: -------------------------------------------------------------------------------- 1 | /* des.c --- DES and Triple-DES encryption/decryption Algorithm 2 | * Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007 3 | * Free Software Foundation, Inc. 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation; either version 2, or (at your 8 | * option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, but 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this file; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18 | * 02110-1301, USA. 19 | * 20 | * ---------------------------------------------------------------------- 21 | * Functions to compute MD4 message digest of files or memory blocks. 22 | * according to the definition of MD4 in RFC 1320 from April 1992. Copyright 23 | * (C) 1995,1996,1997,1999,2000,2001,2002,2003,2005,2006 Free Software 24 | * Foundation, Inc. 25 | * 26 | * This program is free software; you can redistribute it and/or modify it 27 | * under the terms of the GNU General Public License as published by the 28 | * Free Software Foundation; either version 2, or (at your option) any 29 | * later version. 30 | * 31 | * This program is distributed in the hope that it will be useful, 32 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 33 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 34 | * GNU General Public License for more details. 35 | * 36 | * You should have received a copy of the GNU General Public License 37 | * along with this program; if not, write to the Free Software Foundation, 38 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 39 | */ 40 | 41 | #ifndef XCRYPT_H 42 | #define XCRYPT_H 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #define MD5_DIGEST_SIZE 16 50 | #define MD5_BLOCK_SIZE 64 51 | #define IPAD 0x36 52 | #define OPAD 0x5c 53 | 54 | #define gl_des_ecb_encrypt(ctx, from, to) gl_des_ecb_crypt(ctx, from, to, 0) 55 | #define gl_des_ecb_decrypt(ctx, from, to) gl_des_ecb_crypt(ctx, from, to, 1) 56 | 57 | /* 58 | * Encryption/Decryption context of DES 59 | */ 60 | typedef struct { 61 | uint32_t encrypt_subkeys[32]; 62 | uint32_t decrypt_subkeys[32]; 63 | } gl_des_ctx; 64 | 65 | /* Structures to save state of computation between the single steps. */ 66 | struct md4_ctx { 67 | uint32_t A; 68 | uint32_t B; 69 | uint32_t C; 70 | uint32_t D; 71 | 72 | uint32_t total[2]; 73 | uint32_t buflen; 74 | uint32_t buffer[32]; 75 | }; 76 | 77 | struct md5_ctx { 78 | uint32_t A; 79 | uint32_t B; 80 | uint32_t C; 81 | uint32_t D; 82 | 83 | uint32_t total[2]; 84 | uint32_t buflen; 85 | uint32_t buffer[32]; 86 | }; 87 | 88 | extern bool gl_des_is_weak_key(const char * key); 89 | extern void gl_des_setkey(gl_des_ctx *ctx, const char * key); 90 | extern bool gl_des_makekey(gl_des_ctx *ctx, const char * key, size_t keylen); 91 | extern void gl_des_ecb_crypt(gl_des_ctx *ctx, const char * _from, char * _to, int mode); 92 | 93 | extern void md4_process_block (const void *buffer, size_t len, struct md4_ctx *ctx); 94 | extern void md4_init_ctx (struct md4_ctx *ctx); 95 | extern void *md4_read_ctx (const struct md4_ctx *ctx, void *resbuf); 96 | extern void *md4_finish_ctx (struct md4_ctx *ctx, void *resbuf); 97 | extern void md4_process_bytes (const void *buffer, size_t len, struct md4_ctx *ctx); 98 | extern int md4_stream(FILE * stream, void *resblock); 99 | extern void *md4_buffer (const char *buffer, size_t len, void *resblock); 100 | 101 | extern int hmac_md5 (const void *key, size_t keylen, const void *in, size_t inlen, void *resbuf); 102 | 103 | extern void md5_init_ctx (struct md5_ctx *ctx); 104 | extern void md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx); 105 | extern void md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx); 106 | extern void *md5_finish_ctx (struct md5_ctx *ctx, void *resbuf); 107 | extern void *md5_read_ctx (const struct md5_ctx *ctx, void *resbuf); 108 | extern int md5_stream (FILE *stream, void *resblock); 109 | extern void *md5_buffer (const char *buffer, size_t len, void *resblock); 110 | 111 | #endif /* XCRYPT_H */ 112 | -------------------------------------------------------------------------------- /rpm/cntlm.spec: -------------------------------------------------------------------------------- 1 | Summary: Fast NTLM authentication proxy with tunneling 2 | Name: cntlm 3 | Version: 0.94.0 4 | Release: 1%{?dist} 5 | License: GNU GPL V2 6 | %if 0%{?suse_version} 7 | Group: Productivity/Networking/Web/Proxy 8 | %else 9 | Group: System/Daemons 10 | %endif 11 | URL: http://cntlm.sourceforge.net/ 12 | Source0: %{name}-%{version}.tar.bz2 13 | Source1: %{name}.init 14 | Source2: %{name}.sysconfig 15 | 16 | 17 | %if 0%{?suse_version} 18 | Prereq: util-linux %{?insserv_prereq} %{?fillup_prereq} 19 | %else 20 | Prereq: which /sbin/chkconfig 21 | %endif 22 | Prereq: /usr/sbin/useradd /usr/bin/getent 23 | 24 | Provides: cntlm = %{version} 25 | 26 | BuildRoot: %{_tmppath}/%{name}-%{version}-root 27 | 28 | %description 29 | Cntlm is a fast and efficient NTLM proxy, with support for TCP/IP tunneling, 30 | authenticated connection caching, ACLs, proper daemon logging and behaviour 31 | and much more. It has up to ten times faster responses than similar NTLM 32 | proxies, while using by orders or magnitude less RAM and CPU. Manual page 33 | contains detailed information. 34 | 35 | %prep 36 | %setup -q -n %{name}-%{version} 37 | 38 | %build 39 | ./configure 40 | make SYSCONFDIR=%{_sysconfdir} \ 41 | BINDIR=%{_sbindir} \ 42 | MANDIR=%{_mandir} 43 | 44 | %install 45 | # Clean up in case there is trash left from a previous build 46 | rm -rf $RPM_BUILD_ROOT 47 | mkdir $RPM_BUILD_ROOT 48 | 49 | # Create the target build directory hierarchy 50 | %if 0%{?suse_version} 51 | mkdir -p ${RPM_BUILD_ROOT}/var/adm/fillup-templates 52 | %else 53 | mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig 54 | %endif 55 | 56 | mkdir -p $RPM_BUILD_ROOT/sbin 57 | 58 | %makeinstall SYSCONFDIR=$RPM_BUILD_ROOT/%{_sysconfdir} \ 59 | BINDIR=$RPM_BUILD_ROOT/%{_sbindir} \ 60 | MANDIR=$RPM_BUILD_ROOT/%{_mandir} 61 | %if 0%{?suse_version} 62 | install -D -m 755 %{SOURCE1} $RPM_BUILD_ROOT/%{_initrddir}/cntlm 63 | install -D -m 644 %{SOURCE2} $RPM_BUILD_ROOT/var/adm/fillup-templates/sysconfig.cntlm 64 | ln -sf %{_initrddir}/cntlm $RPM_BUILD_ROOT/sbin/rccntlm 65 | %else 66 | install -D -m 755 %{SOURCE1} $RPM_BUILD_ROOT/%{_initrddir}/cntlmd 67 | install -D -m 644 %{SOURCE2} $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/cntlmd 68 | ln -sf %{_initrddir}/cntlmd $RPM_BUILD_ROOT/sbin/rccntlmd 69 | %endif 70 | 71 | %clean 72 | rm -rf $RPM_BUILD_ROOT 73 | 74 | %pre 75 | if [ "$1" -eq 1 ]; then 76 | [ -z "`%{_bindir}/getent passwd "cntlm"`" ] && { 77 | useradd -s /sbin/nologin -m -r -d /var/run/cntlm cntlm 2>/dev/null 78 | } 79 | fi 80 | : 81 | 82 | %post 83 | %if 0%{?suse_version} 84 | %{fillup_and_insserv cntlm} 85 | %else 86 | if [ "$1" -eq 1 ]; then 87 | if [ -x /usr/lib/lsb/install_initd ]; then 88 | /usr/lib/lsb/install_initd /etc/init.d/cntlmd 89 | elif [ -x /sbin/chkconfig ]; then 90 | /sbin/chkconfig --add cntlmd 91 | else 92 | for i in 2 3 4 5; do 93 | ln -sf /etc/init.d/cntlmd /etc/rc.d/rc${i}.d/S26cntlmd 94 | done 95 | for i in 1 6; do 96 | ln -sf /etc/init.d/cntlmd /etc/rc.d/rc${i}.d/K89cntlmd 97 | done 98 | fi 99 | fi 100 | : 101 | %endif 102 | 103 | %preun 104 | %if 0%{?suse_version} 105 | %{stop_on_removal cntlm} 106 | %else 107 | if [ "$1" -eq 0 ]; then 108 | /etc/init.d/cntlmd stop > /dev/null 2>&1 109 | if [ -x /usr/lib/lsb/remove_initd ]; then 110 | /usr/lib/lsb/install_initd /etc/init.d/cntlmd 111 | elif [ -x /sbin/chkconfig ]; then 112 | /sbin/chkconfig --del cntlmd 113 | else 114 | rm -f /etc/rc.d/rc?.d/???cntlmd 115 | fi 116 | fi 117 | : 118 | %endif 119 | 120 | %postun 121 | if [ "$1" -eq 0 ]; then 122 | /usr/sbin/userdel -r cntlm 2>/dev/null 123 | fi 124 | : 125 | %if 0%{?suse_version} 126 | %{insserv_cleanup} 127 | %else 128 | if [ -x /usr/lib/lsb/remove_initd ]; then 129 | /usr/lib/lsb/install_initd /etc/init.d/cntlmd 130 | elif [ -x /sbin/chkconfig ]; then 131 | /sbin/chkconfig --del cntlmd 132 | else 133 | rm -f /etc/rc.d/rc?.d/???cntlmd 134 | fi 135 | : 136 | %endif 137 | 138 | %files 139 | %defattr(-,root,root,-) 140 | %doc LICENSE README COPYRIGHT 141 | %{_sbindir}/cntlm 142 | %{_mandir}/man1/cntlm.1* 143 | %config(noreplace) %{_sysconfdir}/cntlm.conf 144 | %if 0%{?suse_version} 145 | %config(noreplace) /var/adm/fillup-templates/sysconfig.cntlm 146 | %{_initrddir}/cntlm 147 | /sbin/rccntlm 148 | %else 149 | %config(noreplace) %{_sysconfdir}/sysconfig/cntlmd 150 | %{_initrddir}/cntlmd 151 | /sbin/rccntlmd 152 | %endif 153 | 154 | %changelog 155 | * Thu Mar 18 2010 : Version 0.90 156 | - Major rewrite of proxy code 157 | - NoProxy option added to bypass proxy for certain addresses 158 | - Ability to work as a standalone proxy added 159 | - few changes in spec file to package successfully for SuSE 160 | and RedHat distros using openSuSE BuildService by 161 | Michal Strnad 162 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # Copilot Instructions for CNTLM 2 | 3 | This is a summary of the CNTLM codebase, its structure, conventions, and key patterns to help AI agents (like GitHub Copilot) assist effectively. 4 | For more details, see `README.md` and comments in each module. When in doubt, follow the structure and patterns of existing code. 5 | 6 | ## Project Overview 7 | - CNTLM is a fast and efficient NTLM/NTLMv2 authenticating HTTP proxy, written in C for cross-platform use (Linux, Windows, macOS, *BSD, AIX). 8 | - The codebase is organized by functional modules: authentication (`auth.c`), configuration (`config.c`), proxy logic (`proxy.c`), NTLM protocol (`ntlm.c`), and utility functions (`utils.c`). 9 | - Windows-specific and packaging logic is under the `win/` directory; platform compatibility code is in `config/`. 10 | 11 | ## Build & Install 12 | - Standard build: `./configure && make && make install` 13 | - Windows build (Cygwin): 14 | - Install required Cygwin packages: `gcc-core`, `make`, `ghostscript`, `dos2unix`, `zip`, `cygrunsrv`. 15 | - Run: `./configure && make` (from Cygwin shell) 16 | - To create a Windows installer: `make win` (uses InnoSetup script in `win/setup.iss`) 17 | - Packaging: `make deb`, `make rpm`, `make tgz`, `make tbz2` for various package formats. 18 | 19 | ## Key Patterns & Conventions 20 | - System-wide config is loaded from a hardcoded path (default `/etc/cntlm.conf`), unless overridden with `-c`. 21 | - No dynamic library dependencies except for pthreads (threading; required for concurrency, see `utils.c`). 22 | - Platform-specific code is isolated in `config/` and `win/`. 23 | - Endianness is auto-detected at build time (relevant for protocol-level operations). 24 | - All binaries, man pages, and config templates are generated by the build system; see `Makefile` and `configure`. 25 | 26 | ## Testing & CI 27 | - CI is configured for Linux (GitHub Actions) and Windows (AppVeyor). 28 | - Code quality is monitored via SonarCloud, Coverity, Codacy, and CodeQL (see badges in `README.md`). 29 | - No explicit test suite is present. Manual and integration testing is expected: run CNTLM with a sample config, use `curl` or a browser to verify proxying and authentication, and check logs for errors. 30 | 31 | ## Notable Files & Directories 32 | - `main.c`: Entry point, daemon logic. 33 | - `config/`: Platform compatibility shims (e.g., `arc4random_buf.c`, `gethostname.c`). 34 | - `win/`: Windows installer resources and InnoSetup scripts. 35 | - `doc/`: Man page, config samples, and init scripts. 36 | - `debian/`, `rpm/`: Packaging metadata for respective systems. 37 | 38 | ## Contribution & Coding Style 39 | - Follow existing C code style (K&R, 4-space indents, braces on same line). 40 | - Use platform abstraction layers for OS-specific logic. 41 | - Keep all configuration and secrets handling in `config.c` and `config.h`. Always wipe secrets from memory after use with `compat_memset_s` or similar. 42 | - Use `myexit` and `croak` for controlled process termination and error reporting, not raw `exit` or `abort`. 43 | - Document any new build or packaging steps in `README.md`. 44 | 45 | ## Example: Adding a New Auth Method 46 | - Implement protocol logic in a new file (e.g., `auth_foo.c`), update `Makefile`. 47 | - Register the method in `auth.c` and expose config in `config.c`. 48 | - Update documentation in `README.md` and `doc/` as needed. 49 | - When extending protocols or authentication, keep logic isolated and follow the registration pattern in `auth.c`. 50 | 51 | ## Frequently Used Utility Functions 52 | - `zmalloc(size_t size)`: Allocates zero-initialized memory of the given size (wrapper for `calloc`). Used throughout the codebase for safe memory allocation. Returns a pointer to the allocated memory or `NULL` on failure. Example usage: `ptr = zmalloc(nbytes);`. 53 | - `myexit(int rc)`: Terminates the process with the given return code. Used for controlled exits on fatal errors. 54 | - `croak(const char *msg, int console)`: Prints an error message and terminates the process. 55 | - `hlist_*` and `plist_*`: Single-linked list helpers for managing headers and cached connections. See `utils.h` for available operations (add, delete, duplicate, count, etc.). 56 | - `lowercase(char *str)`, `uppercase(char *str)`: In-place string case conversion utilities. 57 | - `compat_memset_s(void *dest, size_t destsz, char ch, size_t count)`: Secure memory clearing function, used to wipe sensitive data (e.g., passwords) from memory. 58 | 59 | Refer to `utils.c` and `utils.h` for more details and additional helpers. When reviewing or generating code, prefer these wrappers over direct use of `malloc`, `free`, or raw memory operations for consistency and safety. 60 | 61 | ## Security Practices 62 | - All code that processes, stores, or manipulates sensitive information—such as passwords, NTLM hashes, or authentication tokens—should be implemented only in `config.c`, `config.h`, or the authentication protocol modules (`auth.c`, `ntlm.c`, `kerberos.c`, etc.). 63 | - Do not handle secrets (e.g., read, write, store, or modify passwords or hashes) in unrelated modules like `proxy.c`, `main.c`, or utility code. This centralizes sensitive logic, making it easier to audit, secure, and ensure secrets are properly wiped from memory. 64 | - Always wipe sensitive data from memory after use. 65 | 66 | ## Threading Model 67 | - CNTLM uses pthreads for concurrency. Thread management helpers and argument structures are in `utils.c` and `utils.h`. 68 | 69 | ## AI Agent Guidance 70 | Whenever you (Copilot or any LLM-based agent or even humans) make or review changes to this codebase, check if these instructions are still accurate. If you notice that something is no longer correct or complete, highlight the issue and suggest corrections or updates to this file. 71 | -------------------------------------------------------------------------------- /pac.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Parsing of pac files 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2022 Francesco MDE aka fralken, David Kubicek 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include "duktape/duktape.h" 26 | #include "pac_utils_js.h" 27 | #include "pac.h" 28 | 29 | /* 30 | * global duktape context 31 | */ 32 | duk_context *pac_ctx = NULL; 33 | 34 | static duk_ret_t native_dnsresolve(duk_context *ctx) { 35 | const char *hostname; 36 | struct addrinfo hints; 37 | struct addrinfo *addresses; 38 | 39 | memset(&hints, 0, sizeof(hints)); 40 | hints.ai_socktype = SOCK_STREAM; 41 | hints.ai_family = AF_INET; 42 | 43 | hostname = duk_to_string(ctx, 0); 44 | 45 | int rc = getaddrinfo(hostname, NULL, &hints, &addresses); 46 | if (rc != 0) { 47 | duk_push_string(ctx, NULL); 48 | } else { 49 | char s[INET_ADDRSTRLEN] = {0}; 50 | for(struct addrinfo *p = addresses; p != NULL; p = p->ai_next) { 51 | if (p->ai_family == AF_INET) { 52 | getnameinfo(p->ai_addr, p->ai_addrlen, s, sizeof(s), NULL, 0, NI_NUMERICHOST); 53 | break; 54 | } 55 | } 56 | duk_push_string(ctx, s); 57 | freeaddrinfo(addresses); 58 | } 59 | 60 | return 1; 61 | } 62 | 63 | static duk_ret_t native_myipaddress(duk_context *ctx) { 64 | struct ifaddrs *addrs; 65 | 66 | int rc = getifaddrs(&addrs); 67 | if (rc != 0) { 68 | duk_push_string(ctx, "127.0.0.1"); 69 | } else { 70 | char s[INET_ADDRSTRLEN] = {0}; 71 | for (struct ifaddrs *p = addrs; p != NULL; p = p->ifa_next) { 72 | if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET) { 73 | getnameinfo(p->ifa_addr, sizeof(struct sockaddr_in), s, sizeof(s), NULL, 0, NI_NUMERICHOST); 74 | } 75 | } 76 | duk_push_string(ctx, s); 77 | freeifaddrs(addrs); 78 | } 79 | 80 | return 1; 81 | } 82 | 83 | char *read_file(const char* filename) { 84 | FILE *fd; 85 | char *buf; 86 | size_t len; 87 | size_t ret; 88 | 89 | fd = fopen(filename, "r"); 90 | if(fd == NULL) 91 | return NULL; 92 | 93 | fseek(fd, 0L, SEEK_END); 94 | len = ftell(fd); 95 | fseek(fd, 0L, SEEK_SET); 96 | 97 | buf = (char*)calloc(len+1, sizeof(char)); 98 | if(buf == NULL) { 99 | fclose(fd); 100 | return NULL; 101 | } 102 | 103 | ret = fread(buf, sizeof(char), len, fd); 104 | if (ret < len) { 105 | free(buf); 106 | buf = NULL; 107 | } 108 | fclose(fd); 109 | 110 | return buf; 111 | } 112 | 113 | // returns an escaped string or null if original string 114 | // does not need to be escaped, or it is null, 115 | // or an allocation error happens 116 | char *escape_string(const char *str) { 117 | if (!str) 118 | return NULL; 119 | 120 | int n = 0; 121 | const char *p = str; 122 | while (*p) { 123 | if (*p == '"' || *p == '\\') 124 | n++; 125 | p++; 126 | } 127 | if (n == 0) 128 | return NULL; 129 | 130 | char *newstr = (char*)calloc(p - str + n + 1, sizeof(char)); 131 | if (!newstr) 132 | return NULL; 133 | 134 | char *q = newstr; 135 | p = str; 136 | while (*p) { 137 | if (*p == '"' || *p == '\\') 138 | *q++ = '\\'; 139 | *q++ = *p++; 140 | } 141 | *q = 0; 142 | return newstr; 143 | } 144 | 145 | int pac_init(void) { 146 | pac_ctx = duk_create_heap_default(); 147 | 148 | if (pac_ctx) { 149 | duk_push_c_function(pac_ctx, native_dnsresolve, 1); 150 | duk_put_global_string(pac_ctx, "dnsResolve"); 151 | duk_push_c_function(pac_ctx, native_myipaddress, 0); 152 | duk_put_global_string(pac_ctx, "myIpAddress"); 153 | 154 | duk_eval_string(pac_ctx, pac_utils_js); 155 | duk_pop(pac_ctx); 156 | } 157 | 158 | return pac_ctx != NULL; 159 | } 160 | 161 | int pac_parse_file(const char *pacfile) { 162 | char *pacstring = read_file(pacfile); 163 | if (!pacstring) 164 | return 0; 165 | 166 | int rc = pac_parse_string(pacstring); 167 | free(pacstring); 168 | 169 | return rc; 170 | } 171 | 172 | int pac_parse_string(const char *pacstring) { 173 | if (!pac_ctx) 174 | return 0; 175 | 176 | duk_eval_string(pac_ctx, pacstring); 177 | duk_pop(pac_ctx); 178 | 179 | return 1; 180 | } 181 | 182 | const char *pac_find_proxy(const char *url, const char *host) { 183 | if (!pac_ctx || !url || !host) 184 | return NULL; 185 | 186 | char* escaped_url = escape_string(url); 187 | char* escaped_host = escape_string(host); 188 | 189 | duk_push_sprintf(pac_ctx, "FindProxyForURL(\"%s\", \"%s\");", 190 | escaped_url ? escaped_url : url, 191 | escaped_host ? escaped_host : host); 192 | duk_eval(pac_ctx); 193 | const char* res = duk_get_string(pac_ctx, -1); 194 | duk_pop(pac_ctx); 195 | 196 | if (escaped_url) 197 | free(escaped_url); 198 | if (escaped_host) 199 | free(escaped_host); 200 | 201 | return res; 202 | } 203 | 204 | void pac_cleanup(void) { 205 | if (pac_ctx) { 206 | duk_destroy_heap(pac_ctx); 207 | pac_ctx = NULL; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Installation using packages 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | Most of the popular distros contain cntlm packages in their repositories. 5 | You can use the procedures described below to prepare a package of current cntlm 6 | version if desired. 7 | 8 | NOTE: generating packages traditionally requires root privileges (to be able to set 9 | proper ownership and permissions on package members). You can overcome that using 10 | fakeroot. However, to install your packages you have to be root. 11 | 12 | *** SOURCE TARBALL *** 13 | 14 | $ make tgz 15 | or 16 | $ make tbz2 17 | 18 | *** DEBIAN PACKAGES *** 19 | 20 | 1) Quick way: 21 | 22 | $ make deb 23 | 24 | 2) From Debian/Ubuntu repository: 25 | 26 | Get these files (e.g. apt-get source cntlm): 27 | 28 | cntlm_0.XX-X.diff.gz 29 | cntlm_0.XX-X.dsc 30 | cntlm_0.XX.orig.tar.gz 31 | 32 | Compile: 33 | 34 | $ dpkg-source -x cntlm_0.XX-Y.dsc 35 | $ cd cntlm-0.XX/ 36 | $ dpkg-buildpackage -b -rfakeroot 37 | 38 | Upon installation, the package takes care of creating a dedicated user for 39 | cntlm, init script integration, manages eventual configuration file updates 40 | with new upstream versions, things like restart of the daemon after future 41 | updates, etc. You can later revert all these changes with one command, should 42 | you decide to remove cntlm from your system. 43 | 44 | 45 | *** RPM FROM SCRATCH *** 46 | 47 | 1) Quick way: 48 | 49 | $ make rpm # you'll need root privs. or fakeroot utility 50 | 51 | 2) Detailed howto (or if make rpm doesn't work for you) 52 | 53 | To build an RPM package from scratch, as root change to 54 | /usr/src/[redhat|rpm|whatever]/SOURCES 55 | 56 | Copy there all files from cntlm's rpm/ directory plus appropriate version of 57 | the source tar.bz2 (see SOURCE TARBALL section above) and type: 58 | 59 | $ rpmbuild -ba cntlm.spec 60 | 61 | Shortly after, you'll have source and binary RPMs ready in your ../SRPMS, resp. 62 | ../RPMS directories. 63 | 64 | If your build cannot find the default config file in /etc, you probably have 65 | broken RPM build environment. You should add this to your ~/.rpmmacros: 66 | %_sysconfdir /etc 67 | 68 | *** RPM FROM *.src.rpm *** 69 | 70 | If you just want to create a binary package from src.rpm, as root type: 71 | 72 | $ rpmbuild --rebuild pkgname.src.rpm 73 | 74 | Resulting binary RPM will be at /usr/src/..../RPMS 75 | 76 | If your build cannot find the default config file in /etc, you probably have 77 | broken RPM build environment. You should add this to your ~/.rpmmacros: 78 | %_sysconfdir /etc 79 | 80 | *** WINDOWS INSTALLER *** 81 | 82 | Install CygWin and include at least the ghostscript, zip, dos2unix and libgcc 83 | packages. 84 | 85 | In case you are using a 64-bit version of CygWin: rename cyggcc_s-1.dll to 86 | cyggcc_s-seh-1.dll in Makefile and win/setup.iss. 87 | 88 | Start a CygWin console by using the shortcut on your desktop or startup menu. 89 | 90 | From within the CygWin command shell: 91 | 92 | $ cd /cygdrive/yourdrive/your_ctnlm_src_location 93 | 94 | $ ./configure 95 | $ make 96 | 97 | Prepare all binaries, manuals, config templates, Start Menu links and InnoSetup 98 | project definition file: 99 | 100 | $ make win 101 | 102 | Now this automatically creates the installer. 103 | For manually creating the installer you can do this: 104 | Run InnoSetup compiler to pack it all into an automatic installer EXE: 105 | $ /your/path/to/ISCC.exe win/setup.iss 106 | or 107 | Open folder "win" in explorer, right click "setup.iss" and select "Compile". 108 | Both will generate an installer in the "cntlm" folder. 109 | 110 | Traditional installation 111 | ~~~~~~~~~~~~~~~~~~~~~~~~ 112 | First, you have to compile cntlm. Using the Makefile, this should be very easy: 113 | 114 | $ ./configure 115 | $ make 116 | $ make install 117 | 118 | Cntlm does not require any dynamic libraries and there are no dependencies you 119 | have to satisfy before compilation, except for libpthreads. This library is 120 | required for all threaded applications and is very likely to be part of your 121 | system already, because it comes with libc. Next, install cntlm onto your 122 | system like so: 123 | 124 | Default installation directories are /usr/local/sbin, /usr/local/share/man and /etc. 125 | Should you want to install cntlm into a different location, change the DESTDIR 126 | installation prefix (from "/") to add a different installation prefix. 127 | To change the location of binaries and manual (from "/usr/local") use PREFIX. 128 | To change individual directories, use BINDIR, MANDIR and SYSCONFDIR: 129 | 130 | $ make SYSCONFDIR=/etc BINDIR=/usr/bin MANDIR=/usr/share/man 131 | $ make install SYSCONFDIR=/etc BINDIR=/usr/bin MANDIR=/usr/share/man 132 | 133 | Cntlm is compiled with system-wide configuration file by default. That means 134 | whenever you run cntlm, it looks into a hardcoded path (SYSCONFDIR) and tries 135 | to load cntml.conf. You cannot make it not to do so, unless you use -c with an 136 | alternative file or /dev/null. This is standard behaviour and probably what you 137 | want. On the other hand, some of you might not want to use cntlm as a daemon 138 | started by init scripts and you would prefer setting up everything on the 139 | command line. This is possible, just comment out SYSCONFDIR variable definition 140 | in the Makefile before you compile cntlm and it will remove this feature. 141 | 142 | Installation includes the main binary, the man page (see "man cntlm") and if 143 | the default config feature was not removed, it also installs a configuration 144 | template. Please note that unlike bin and man targets, existing configuration 145 | is never overwritten during installation. In the doc/ directory you can find 146 | among other things a file called "cntlmd". It can be used as an init.d script. 147 | 148 | 149 | Architectures 150 | ~~~~~~~~~~~~~ 151 | The build system now has an autodetection of the build arch endianness. Every 152 | common CPU and OS out there is supported, including Windows, MacOS X, Linux, 153 | *BSD, AIX. 154 | 155 | 156 | Compilers 157 | ~~~~~~~~~ 158 | Cntlm is tested against GCC and IBM XL C/C++, other C compilers will work 159 | for you too. There are no compiler specific directives and options AFAIK. 160 | compilers might work for you (then again, they might not). Specific 161 | Makefiles for different compilers are supported by the ./configure script 162 | (e.g. Makefile.xlc) 163 | 164 | 165 | Contact 166 | ~~~~~~~ 167 | David Kubicek 168 | -------------------------------------------------------------------------------- /socket.c: -------------------------------------------------------------------------------- 1 | /* 2 | * These are socket routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "socket.h" 33 | 34 | extern int debug; 35 | 36 | /* 37 | * getaddrinfo() wrapper. Return 1 if OK, otherwise 0. 38 | * Important: Caller is responsible for freeing addresses via freeaddrinfo()! 39 | */ 40 | int so_resolv(struct addrinfo **addresses, const char *hostname, const int port) { 41 | struct addrinfo hints, *p; 42 | char buf[6]; 43 | 44 | memset(&hints, 0, sizeof(hints)); 45 | hints.ai_socktype = SOCK_STREAM; 46 | 47 | snprintf(buf, sizeof(buf), "%d", (uint16_t)port); 48 | int rc = getaddrinfo(hostname, buf, &hints, addresses); 49 | if (rc != 0) { 50 | if (debug) 51 | printf("so_resolv: %s failed: %s (%d)\n", hostname, gai_strerror(rc), rc); 52 | return 0; 53 | } 54 | 55 | if (debug) { 56 | char s[INET6_ADDRSTRLEN] = {0}; 57 | printf("Resolve %s:\n", hostname); 58 | for (p = *addresses; p != NULL; p = p->ai_next) { 59 | INET_NTOP(p->ai_addr, s, INET6_ADDRSTRLEN); 60 | printf(" %s\n", s); 61 | } 62 | } 63 | 64 | return 1; 65 | } 66 | 67 | /* 68 | * getaddrinfo() wrapper, wildcard mode. If "gateway" is 0 the network address 69 | * will be set to the loopback interface address, otherwise it will contain 70 | * the "wildcard address" (gateway mode). 71 | * Important: Caller is responsible for freeing addresses via freeaddrinfo()! 72 | */ 73 | int so_resolv_wildcard(struct addrinfo **addresses, const int port, int gateway) { 74 | struct addrinfo hints; 75 | char buf[6]; 76 | 77 | snprintf(buf, sizeof(buf), "%d", (uint16_t)port); 78 | 79 | memset(&hints, 0, sizeof(hints)); 80 | hints.ai_socktype = SOCK_STREAM; 81 | if (gateway) { 82 | hints.ai_flags = AI_PASSIVE; 83 | } 84 | 85 | return getaddrinfo(NULL, buf, &hints, addresses); 86 | } 87 | 88 | /* 89 | * Connect to a host. 90 | * Returns: socket descriptor 91 | */ 92 | int so_connect(struct addrinfo *addresses) { 93 | int fd = -1; 94 | int rc; 95 | struct addrinfo *p; 96 | char s[INET6_ADDRSTRLEN] = {0}; 97 | 98 | for (p = addresses; p != NULL; p = p->ai_next) { 99 | int flags; 100 | if ((fd = socket(p->ai_family, SOCK_STREAM, 0)) < 0) { 101 | if (debug) 102 | printf("so_connect: create: %s\n", strerror(errno)); 103 | return -1; 104 | } 105 | 106 | if (debug) { 107 | INET_NTOP(p->ai_addr, s, INET6_ADDRSTRLEN); 108 | unsigned short port = INET_PORT(p->ai_addr); 109 | 110 | printf("so_connect: %s : %i \n", s, ntohs(port)); 111 | } 112 | 113 | if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { 114 | if (debug) 115 | printf("so_connect: get flags: %s\n", strerror(errno)); 116 | close(fd); 117 | continue; 118 | } 119 | 120 | /* NON-BLOCKING connect with timeout 121 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { 122 | if (debug) 123 | printf("so_connect: set non-blocking: %s\n", strerror(errno)); 124 | close(fd); 125 | continue; 126 | } 127 | */ 128 | 129 | rc = connect(fd, p->ai_addr, p->ai_addrlen); 130 | 131 | if (rc < 0) { 132 | if (debug) 133 | printf("so_connect: %s\n", strerror(errno)); 134 | close(fd); 135 | fd = -1; 136 | continue; 137 | } 138 | 139 | if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { 140 | if (debug) 141 | printf("so_connect: set blocking: %s\n", strerror(errno)); 142 | close(fd); 143 | fd = -1; 144 | continue; 145 | } 146 | 147 | break; 148 | } 149 | 150 | return fd; 151 | } 152 | 153 | /* 154 | * Bind the specified port and listen on it. 155 | * Retruns: number of successful binds 156 | */ 157 | int so_listen(plist_t *list, struct addrinfo *addresses, void *aux) { 158 | socklen_t clen; 159 | struct addrinfo *p; 160 | char s[INET6_ADDRSTRLEN] = {0}; 161 | int count = 0; 162 | 163 | for (p = addresses; p != NULL; p = p->ai_next) { 164 | int fd = socket(p->ai_family, SOCK_STREAM, 0); 165 | if (fd < 0) { 166 | if (debug) 167 | printf("so_listen: new socket: %s\n", strerror(errno)); 168 | close(fd); 169 | continue; 170 | } 171 | 172 | clen = 1; 173 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &clen, sizeof(clen)) != 0) { 174 | syslog(LOG_WARNING, "setsockopt() (option: SO_REUSEADDR, value: 1) failed: %s\n", strerror(errno)); 175 | } 176 | 177 | if (p->ai_family == AF_INET6) { 178 | clen = 1; 179 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &clen, sizeof(clen)) != 0) { 180 | syslog(LOG_WARNING, "setsockopt() (option: IPV6_V6ONLY, value: 1) failed: %s\n", strerror(errno)); 181 | } 182 | } 183 | 184 | INET_NTOP(p->ai_addr, s, INET6_ADDRSTRLEN); 185 | unsigned short port = INET_PORT(p->ai_addr); 186 | 187 | if (bind(fd, p->ai_addr, p->ai_addrlen)) { 188 | syslog(LOG_ERR, "Cannot bind address %s port %d: %s!\n", s, ntohs(port), strerror(errno)); 189 | close(fd); 190 | continue; 191 | } 192 | 193 | if (listen(fd, SOMAXCONN)) { 194 | close(fd); 195 | continue; 196 | } 197 | 198 | *list = plist_add(*list, fd, aux); 199 | syslog(LOG_INFO, "so_listen: listening on %s:%d\n", s, ntohs(port)); 200 | ++count; 201 | } 202 | 203 | return count; 204 | } 205 | 206 | /* 207 | * Return 1 if data is available on the socket, 208 | * 0 if connection was closed 209 | * -1 if error (errno is set) 210 | */ 211 | int so_recvtest(int fd) { 212 | char buf; 213 | int i; 214 | #ifndef MSG_DONTWAIT 215 | unsigned int flags; 216 | 217 | flags = fcntl(fd, F_GETFL); 218 | fcntl(fd, F_SETFL, flags | O_NONBLOCK); 219 | i = recv(fd, &buf, 1, MSG_PEEK); 220 | fcntl(fd, F_SETFL, flags); 221 | #else 222 | i = recv(fd, &buf, 1, MSG_DONTWAIT | MSG_PEEK); 223 | #endif 224 | 225 | return i; 226 | } 227 | 228 | /* 229 | * Return true if there are some data on the socket 230 | */ 231 | int so_dataready(int fd) { 232 | return so_recvtest(fd) > 0; 233 | } 234 | 235 | /* 236 | * Reliable way of finding out whether a connection was closed 237 | * on the remote end, without actually reading from it. 238 | */ 239 | int so_closed(int fd) { 240 | int i; 241 | 242 | if (fd == -1) 243 | return 1; 244 | 245 | i = so_recvtest(fd); 246 | return (i == 0 || (i == -1 && errno != EAGAIN && errno != ENOENT)); /* ENOENT, you ask? Perhaps AIX devels could explain! :-( */ 247 | } 248 | 249 | /* 250 | * Receive a single line from the socket. This is no super-efficient 251 | * implementation, but more than we need to read in a few headers. 252 | * What's more, the data is actually recv'd from a socket buffer. 253 | * 254 | * I had to time this in comparison to recv with block read :) and 255 | * the performance was very similar. Given the fact that it keeps us 256 | * from creating a whole buffering scheme around the socket (HTTP 257 | * connection is both line and block oriented, switching back and forth), 258 | * it is actually OK. 259 | */ 260 | int so_recvln(int fd, char **buf, int *size) { 261 | int len = 0; 262 | int r = 1; 263 | char c = 0; 264 | char *tmp; 265 | 266 | while (len < *size-1 && c != '\n') { 267 | r = read(fd, &c, 1); 268 | if (r <= 0) 269 | break; 270 | 271 | (*buf)[len++] = c; 272 | 273 | /* 274 | * End of buffer, still no EOL? Resize the buffer 275 | */ 276 | if (len == *size-1 && c != '\n') { 277 | if (debug) 278 | printf("so_recvln(%d): realloc %d\n", fd, *size*2); 279 | *size *= 2; 280 | tmp = realloc(*buf, *size); 281 | if (tmp == NULL) 282 | return -1; 283 | else 284 | *buf = tmp; 285 | } 286 | } 287 | (*buf)[len] = 0; 288 | 289 | return r; 290 | } 291 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * These are helping routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #ifndef UTILS_H 23 | #define UTILS_H 24 | 25 | #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 26 | # include 27 | #endif 28 | #include 29 | #include 30 | 31 | #include "config/config.h" 32 | 33 | #define BUFSIZE 4096 34 | #define MINIBUF_SIZE 64 35 | /* 36 | * Longest password that appears to be supported in a Microsoft authn/authz implementation is 256 characters; 37 | * therefore support passwords up to 256 characters plus null terminator. 38 | * source: https://learn.microsoft.com/en-us/entra/identity/authentication/concept-password-ban-bad-combined-policy#azure-ad-password-policies 39 | */ 40 | #define PASSWORD_BUFSIZE 257 41 | #define HOST_BUFSIZE 260 42 | #define VAL(var, type, offset) *((type *)(var+offset)) 43 | #define MEM(var, type, offset) (type *)(var+offset) 44 | 45 | #if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) 46 | # define MIN(a, b) ((a) < (b) ? (a) : (b)) 47 | # define MAX(a, b) ((a) > (b) ? (a) : (b)) 48 | #endif 49 | 50 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 51 | 52 | /* 53 | * Solaris doesn't have LOG_PERROR 54 | */ 55 | #ifndef LOG_PERROR 56 | # define LOG_PERROR LOG_CONS 57 | #endif 58 | 59 | /* 60 | * Two single-linked list types. First is for storing headers, 61 | * second keeps a list of finished threads or cached connections. 62 | * Each has a different set of manipulation routines. 63 | */ 64 | typedef struct hlist_s *hlist_t; 65 | typedef const struct hlist_s *hlist_const_t; 66 | struct hlist_s { 67 | char *key; 68 | char *value; 69 | int islist; 70 | struct hlist_s *next; 71 | }; 72 | 73 | typedef struct plist_s *plist_t; 74 | typedef const struct plist_s *plist_const_t; 75 | struct plist_s { 76 | unsigned long key; 77 | void *aux; 78 | struct plist_s *next; 79 | }; 80 | 81 | typedef enum { 82 | HLIST_NOALLOC = 0, 83 | HLIST_ALLOC 84 | } hlist_add_t; 85 | 86 | /* 87 | * Request/response data structure. Complete and parsed req/res is 88 | * kept in this. See below for (de)allocation routines. 89 | */ 90 | typedef struct rr_data_s *rr_data_t; 91 | typedef const struct rr_data_s *rr_data_const_t; 92 | struct rr_data_s { 93 | int req; 94 | hlist_t headers; 95 | int code; 96 | int skip_http; 97 | int body_len; 98 | int empty; 99 | int port; 100 | int http_version; 101 | char *method; 102 | char *url; 103 | char *rel_url; 104 | char *hostname; 105 | char *http; 106 | char *msg; 107 | char *body; 108 | char *errmsg; 109 | }; 110 | 111 | /* 112 | * This structure can represent a sockaddr or 113 | * an AF_INET (ipv4) sockaddr or an AF_INET6 (ipv6) sockaddr 114 | */ 115 | union sock_addr { 116 | struct sockaddr addr; 117 | struct sockaddr_in addr_in; 118 | struct sockaddr_in6 addr_in6; 119 | }; 120 | 121 | /* 122 | * This is used in main() for passing arguments to the thread. 123 | */ 124 | struct thread_arg_s { 125 | int fd; 126 | char *target; 127 | union sock_addr addr; 128 | }; 129 | 130 | /* 131 | * Returns the string representation of an inet or inet6 address (determined by the family). 132 | * (sa) must be a pointer to sockaddr_in or sockaddr_in6. 133 | * (s) must be a char array of at least INET6_ADDRSTRLEN size. 134 | * (len) is the length of (s). 135 | */ 136 | #define INET_NTOP(sa, s, len) \ 137 | (inet_ntop(((struct sockaddr*)(sa))->sa_family, \ 138 | ((struct sockaddr*)(sa))->sa_family == AF_INET ? (void*)&((struct sockaddr_in*)(sa))->sin_addr : (void*)&((struct sockaddr_in6*)(sa))->sin6_addr, (s), (len))) 139 | 140 | /* 141 | * Returns the port of an inet or inet6 address (determined by the family). 142 | * (sa) must be a pointer to sockaddr_in or sockaddr_in6. 143 | */ 144 | #define INET_PORT(sa) \ 145 | (((struct sockaddr*)(sa))->sa_family == AF_INET ? ((struct sockaddr_in*)(sa))->sin_port : ((struct sockaddr_in6*)(sa))->sin6_port) 146 | 147 | extern void myexit(int rc) __attribute__((noreturn)); 148 | extern void croak(const char *msg, const int console) __attribute__((noreturn)); 149 | 150 | extern plist_t plist_add(plist_t list, unsigned long key, void *aux); 151 | extern plist_t plist_del(plist_t list, const unsigned long key); 152 | extern int plist_in(plist_const_t list, const unsigned long key) __attribute__((warn_unused_result)); 153 | extern void plist_dump(plist_const_t list); 154 | extern char *plist_get(plist_const_t list, const unsigned long key) __attribute__((warn_unused_result)); 155 | extern int plist_pop(plist_t *list, void **aux); 156 | extern int plist_count(plist_const_t list) __attribute__((warn_unused_result)); 157 | extern plist_t plist_free(plist_t list); 158 | 159 | extern hlist_t hlist_add(hlist_t list, char *key, char *value, hlist_add_t allockey, hlist_add_t allocvalue); 160 | extern hlist_t hlist_dup(hlist_const_t list) __attribute__((warn_unused_result)); 161 | extern hlist_t hlist_del(hlist_t list, const char *key); 162 | extern hlist_t hlist_mod(hlist_t list, char *key, char *value, int add); 163 | extern int hlist_in(hlist_const_t list, const char *key) __attribute__((warn_unused_result)); 164 | extern int hlist_count(hlist_const_t list) __attribute__((warn_unused_result)); 165 | extern char *hlist_get(hlist_const_t list, const char *key) __attribute__((warn_unused_result)); 166 | extern int hlist_subcmp(hlist_const_t list, const char *key, const char *substr) __attribute__((warn_unused_result)); 167 | extern int hlist_subcmp_all(hlist_const_t list, const char *key, const char *substr) __attribute__((warn_unused_result)); 168 | extern hlist_t hlist_free(hlist_t list); 169 | extern void hlist_dump(hlist_const_t list); 170 | 171 | extern char *substr(const char *src, int pos, int len) __attribute__((warn_unused_result)); 172 | #if config_strlcpy == 0 173 | extern size_t strlcpy(char *dst, const char *src, size_t siz); 174 | #endif 175 | #if config_strlcat == 0 176 | extern size_t strlcat(char *dst, const char *src, size_t siz); 177 | #endif 178 | extern char *trimr(char * const buf); 179 | extern char *lowercase(char * const str); 180 | extern char *uppercase(char * const str); 181 | extern size_t unicode(char **dst, const char * const src); 182 | extern void *zmalloc(size_t size) __attribute__((warn_unused_result, malloc, alloc_size(1))); 183 | extern char *urlencode(const char * const str) __attribute__((warn_unused_result)); 184 | 185 | extern rr_data_t new_rr_data(void) __attribute__((warn_unused_result)); 186 | extern rr_data_t copy_rr_data(rr_data_t dst, const rr_data_const_t src); 187 | extern rr_data_t dup_rr_data(const rr_data_const_t data) __attribute__((warn_unused_result)); 188 | extern rr_data_t reset_rr_data(rr_data_t data); 189 | extern void free_rr_data(rr_data_t * data); 190 | 191 | extern char *printmem(const char * const src, const size_t len, const int bitwidth) __attribute__((warn_unused_result)); 192 | extern char *scanmem(const char * const src, const int bitwidth) __attribute__((warn_unused_result)); 193 | 194 | extern int is_memory_all_zero(const void * const p_memory, const size_t length) __attribute__((warn_unused_result, pure)); 195 | 196 | extern void to_base64(unsigned char *out, const unsigned char *in, size_t len, size_t olen); 197 | extern int from_base64(char *out, const char *in); 198 | 199 | extern uint64_t getrandom64(void) __attribute__((warn_unused_result)); 200 | 201 | extern ssize_t write_wrapper(int fildes, const void *buf, const size_t nbyte); 202 | 203 | extern void compat_memset_s( void *dest, size_t destsz, char ch, size_t count ); 204 | 205 | #if config_strdup == 0 206 | extern char *strdup(const char *src) __attribute__((warn_unused_result)); 207 | #endif 208 | 209 | #endif /* UTILS_H */ 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cntlm 2 | 3 | |Linux Build|AppVeyor Build (Cygwin)|Coverity Scan|Codacy Analysis|CodeQL|License| 4 | |:--:|:--:|:--:|:--:|:--:|:--:| 5 | |[![C/C++ CI](https://github.com/versat/cntlm/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/versat/cntlm/actions/workflows/c-cpp.yml)|[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/rthu5vjr0ksalyls/branch/master?svg=true)](https://ci.appveyor.com/project/versat/cntlm/branch/master)|[![Coverity Scan Build Status](https://img.shields.io/coverity/scan/15940.svg)](https://scan.coverity.com/projects/versat-cntlm)|[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c506885b133047d38cd2c9dd4505320b)](https://app.codacy.com/gh/versat/cntlm/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)|[![CodeQL](https://github.com/versat/cntlm/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/versat/cntlm/actions/workflows/codeql-analysis.yml)|[![License](https://img.shields.io/badge/license-GPL2.0-blue.svg)](https://opensource.org/licenses/GPL-2.0)| 6 | 7 | SonarCloud: 8 | [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=versat_cntlm&metric=ncloc)](https://sonarcloud.io/dashboard?id=versat_cntlm) 9 | [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=versat_cntlm&metric=bugs)](https://sonarcloud.io/dashboard?id=versat_cntlm) 10 | [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=versat_cntlm&metric=code_smells)](https://sonarcloud.io/dashboard?id=versat_cntlm) 11 | [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=versat_cntlm&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=versat_cntlm) 12 | [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=versat_cntlm&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=versat_cntlm) 13 | [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=versat_cntlm&metric=security_rating)](https://sonarcloud.io/dashboard?id=versat_cntlm) 14 | 15 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/versat/cntlm) 16 | 17 | ## Installation using packages 18 | 19 | Most of the popular distros contain cntlm packages in their repositories. 20 | For Windows an installer is available. 21 | 22 | You can use the procedures described below to prepare a package of current cntlm 23 | version if desired. 24 | 25 | NOTE: generating packages traditionally requires root privileges (to be able to set 26 | proper ownership and permissions on package members). You can overcome that using 27 | fakeroot. However, to install your packages you have to be root. 28 | 29 | ## Creating a SOURCE TARBALL 30 | 31 | make tgz 32 | 33 | or 34 | 35 | make tbz2 36 | 37 | ## Creating DEBIAN PACKAGES 38 | 39 | ### 1) Quick way to create debian package 40 | 41 | make deb 42 | 43 | ### 2) From Debian/Ubuntu repository 44 | 45 | Get these files (e.g. apt-get source cntlm): 46 | 47 | cntlm_0.XX-X.diff.gz 48 | cntlm_0.XX-X.dsc 49 | cntlm_0.XX.orig.tar.gz 50 | 51 | Compile: 52 | 53 | dpkg-source -x cntlm_0.XX-Y.dsc 54 | cd cntlm-0.XX/ 55 | dpkg-buildpackage -b -rfakeroot 56 | 57 | Upon installation, the package takes care of creating a dedicated user for 58 | cntlm, init script integration, manages eventual configuration file updates 59 | with new upstream versions, things like restart of the daemon after future 60 | updates, etc. You can later revert all these changes with one command, should 61 | you decide to remove cntlm from your system. 62 | 63 | ## Creating RPM FROM SCRATCH 64 | 65 | ### 1) Quick way to create RPM 66 | 67 | make rpm # you'll need root privs. or fakeroot utility 68 | 69 | ### 2) Detailed howto (or if make rpm doesn't work for you) 70 | 71 | To build an RPM package from scratch, as root change to 72 | /usr/src/[redhat|rpm|whatever]/SOURCES 73 | 74 | Copy there all files from cntlm's rpm/ directory plus appropriate version of 75 | the source tar.bz2 (see Creating a SOURCE TARBALL section above) and type: 76 | 77 | rpmbuild -ba cntlm.spec 78 | 79 | Shortly after, you'll have source and binary RPMs ready in your ../SRPMS, resp. 80 | ../RPMS directories. 81 | 82 | If your build cannot find the default config file in /etc, you probably have 83 | broken RPM build environment. You should add this to your ~/.rpmmacros: 84 | 85 | %_sysconfdir /etc 86 | 87 | ## Creating RPM FROM *.src.rpm 88 | 89 | If you just want to create a binary package from src.rpm, as root type: 90 | 91 | rpmbuild --rebuild pkgname.src.rpm 92 | 93 | Resulting binary RPM will be at /usr/src/..../RPMS 94 | 95 | If your build cannot find the default config file in /etc, you probably have 96 | broken RPM build environment. You should add this to your ~/.rpmmacros: 97 | 98 | %_sysconfdir /etc 99 | 100 | ## Creating WINDOWS INSTALLER 101 | 102 | Install Cygwin and include at least the gcc-core, make, ghostscript, dos2unix, zip, and cygrunsrv 103 | packages using, for example, the following options: 104 | 105 | setup-x86_64.exe -qgdO -l C:\cygwin64\var\cache\setup -R C:\cygwin64 -s http://cygwin.mirror.constant.com -P gcc-core -P make -P ghostscript -P dos2unix -P zip -P cygrunsrv 106 | 107 | Start a Cygwin console by using the shortcut on your desktop or startup menu. 108 | 109 | From within the Cygwin command shell: 110 | 111 | cd /cygdrive/yourdrive/your_ctnlm_src_location 112 | export CC=gcc 113 | ./configure 114 | make 115 | 116 | Prepare all binaries, manuals, config templates, Start Menu links, InnoSetup 117 | project definition file, installer: 118 | 119 | make win 120 | 121 | Now this automatically creates the installer. 122 | 123 | Alternative, run this command, which does these steps, too: 124 | 125 | C:\cygwin64\bin\bash -e -l -c "cd /cygdrive/yourdrive/your_ctnlm_src_location && make distclean && ./configure && make && make win" 126 | 127 | For manually creating the installer you can do this: 128 | 129 | Run InnoSetup compiler to pack it all into an automatic installer EXE: 130 | 131 | /your/path/to/ISCC.exe win/setup.iss 132 | 133 | or 134 | 135 | Open folder "win" in explorer, right click "setup.iss" and select "Compile". 136 | 137 | Both will generate an installer in the "cntlm" folder. 138 | 139 | ## Traditional installation 140 | 141 | First, you have to compile cntlm. Using the Makefile, this should be very easy: 142 | 143 | ./configure 144 | make 145 | make install 146 | 147 | Cntlm does not require any dynamic libraries and there are no dependencies you 148 | have to satisfy before compilation, except for libpthreads. This library is 149 | required for all threaded applications and is very likely to be part of your 150 | system already, because it comes with libc. Next, install cntlm onto your 151 | system like so: 152 | 153 | Default installation directories are /usr/local/sbin, /usr/local/share/man and /etc. 154 | Should you want to install cntlm into a different location, change the DESTDIR 155 | installation prefix (from "/") to add a different installation prefix. 156 | To change the location of binaries and manual (from "/usr/local") use PREFIX. 157 | To change individual directories, use BINDIR, MANDIR and SYSCONFDIR: 158 | 159 | make SYSCONFDIR=/etc BINDIR=/usr/bin MANDIR=/usr/share/man 160 | make install SYSCONFDIR=/etc BINDIR=/usr/bin MANDIR=/usr/share/man 161 | 162 | Cntlm is compiled with system-wide configuration file by default. That means 163 | whenever you run cntlm, it looks into a hardcoded path (SYSCONFDIR) and tries 164 | to load cntml.conf. You cannot make it not to do so, unless you use -c with an 165 | alternative file or /dev/null. This is standard behavior and probably what you 166 | want. On the other hand, some of you might not want to use cntlm as a daemon 167 | started by init scripts and you would prefer setting up everything on the 168 | command line. This is possible, just comment out SYSCONFDIR variable definition 169 | in the Makefile before you compile cntlm and it will remove this feature. 170 | 171 | Installation includes the main binary, the man page (see "man cntlm") and if 172 | the default config feature was not removed, it also installs a configuration 173 | template. Please note that unlike bin and man targets, existing configuration 174 | is never overwritten during installation. In the doc/ directory you can find 175 | among other things a file called "cntlmd". It can be used as an init.d script. 176 | 177 | ## Architectures 178 | 179 | The build system now has an autodetection of the build arch endianness. Every 180 | common CPU and OS out there is supported, including Windows, MacOS X, Linux, 181 | *BSD, AIX. 182 | 183 | ## Compilers 184 | 185 | Cntlm is tested against GCC, Clang and IBM XL C/C++, other C compilers will work 186 | for you too. There are no compiler specific directives and options AFAIK. 187 | compilers might work for you (then again, they might not). Specific 188 | Makefiles for different compilers are supported by the ./configure script 189 | (e.g. Makefile.xlc) 190 | 191 | ## Contact 192 | 193 | David Kubicek (seems to be no longer available) 194 | -------------------------------------------------------------------------------- /pac_utils_js.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Parsing of pac files 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2022 Francesco MDE aka fralken, David Kubicek 19 | * 20 | */ 21 | 22 | /* 23 | * https://searchfox.org/mozilla-central/source/netwerk/base/ascii_pac_utils.js 24 | * https://hg.mozilla.org/mozilla-central/raw-file/tip/netwerk/base/ascii_pac_utils.js 25 | */ 26 | 27 | /* This Source Code Form is subject to the terms of the Mozilla Public 28 | * License, v. 2.0. If a copy of the MPL was not distributed with this 29 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 30 | 31 | static const char *const pac_utils_js = 32 | "function dnsDomainIs(host, domain) {\n" 33 | " return (\n" 34 | " host.length >= domain.length &&\n" 35 | " host.substring(host.length - domain.length) == domain\n" 36 | " );\n" 37 | "}\n" 38 | 39 | "function dnsDomainLevels(host) {\n" 40 | " return host.split(\".\").length - 1;\n" 41 | "}\n" 42 | 43 | "function isValidIpAddress(ipchars) {\n" 44 | " var matches = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipchars);\n" 45 | " if (matches == null) {\n" 46 | " return false;\n" 47 | " } else if (\n" 48 | " matches[1] > 255 ||\n" 49 | " matches[2] > 255 ||\n" 50 | " matches[3] > 255 ||\n" 51 | " matches[4] > 255\n" 52 | " ) {\n" 53 | " return false;\n" 54 | " }\n" 55 | " return true;\n" 56 | "}\n" 57 | 58 | "function convert_addr(ipchars) {\n" 59 | " var bytes = ipchars.split(\".\");\n" 60 | " var result =\n" 61 | " ((bytes[0] & 0xff) << 24) |\n" 62 | " ((bytes[1] & 0xff) << 16) |\n" 63 | " ((bytes[2] & 0xff) << 8) |\n" 64 | " (bytes[3] & 0xff);\n" 65 | " return result;\n" 66 | "}\n" 67 | 68 | "function isInNet(ipaddr, pattern, maskstr) {\n" 69 | " if (!isValidIpAddress(pattern) || !isValidIpAddress(maskstr)) {\n" 70 | " return false;\n" 71 | " }\n" 72 | " if (!isValidIpAddress(ipaddr)) {\n" 73 | " ipaddr = dnsResolve(ipaddr);\n" 74 | " if (ipaddr == null) {\n" 75 | " return false;\n" 76 | " }\n" 77 | " }\n" 78 | " var host = convert_addr(ipaddr);\n" 79 | " var pat = convert_addr(pattern);\n" 80 | " var mask = convert_addr(maskstr);\n" 81 | " return (host & mask) == (pat & mask);\n" 82 | "}\n" 83 | 84 | "function isPlainHostName(host) {\n" 85 | " return host.search(\"(\\\\.)|:\") == -1;\n" 86 | "}\n" 87 | 88 | "function isResolvable(host) {\n" 89 | " var ip = dnsResolve(host);\n" 90 | " return ip != null;\n" 91 | "}\n" 92 | 93 | "function localHostOrDomainIs(host, hostdom) {\n" 94 | " return host == hostdom || hostdom.lastIndexOf(host + \".\", 0) == 0;\n" 95 | "}\n" 96 | 97 | "function shExpMatch(url, pattern) {\n" 98 | " pattern = pattern.replace(/\\./g, \"\\\\.\");\n" 99 | " pattern = pattern.replace(/\\*/g, \".*\");\n" 100 | " pattern = pattern.replace(/\\?/g, \".\");\n" 101 | " var newRe = new RegExp(\"^\" + pattern + \"$\");\n" 102 | " return newRe.test(url);\n" 103 | "}\n" 104 | 105 | "var wdays = { SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6 };\n" 106 | "var months = {\n" 107 | " JAN: 0,\n" 108 | " FEB: 1,\n" 109 | " MAR: 2,\n" 110 | " APR: 3,\n" 111 | " MAY: 4,\n" 112 | " JUN: 5,\n" 113 | " JUL: 6,\n" 114 | " AUG: 7,\n" 115 | " SEP: 8,\n" 116 | " OCT: 9,\n" 117 | " NOV: 10,\n" 118 | " DEC: 11,\n" 119 | "};\n" 120 | 121 | "function weekdayRange() {\n" 122 | " function getDay(weekday) {\n" 123 | " if (weekday in wdays) {\n" 124 | " return wdays[weekday];\n" 125 | " }\n" 126 | " return -1;\n" 127 | " }\n" 128 | " var date = new Date();\n" 129 | " var argc = arguments.length;\n" 130 | " var wday;\n" 131 | " if (argc < 1) {\n" 132 | " return false;\n" 133 | " }\n" 134 | " if (arguments[argc - 1] == \"GMT\") {\n" 135 | " argc--;\n" 136 | " wday = date.getUTCDay();\n" 137 | " } else {\n" 138 | " wday = date.getDay();\n" 139 | " }\n" 140 | " var wd1 = getDay(arguments[0]);\n" 141 | " var wd2 = argc == 2 ? getDay(arguments[1]) : wd1;\n" 142 | " if (wd1 == -1 || wd2 == -1) {\n" 143 | " return false;\n" 144 | " }\n" 145 | 146 | " if (wd1 <= wd2) {\n" 147 | " return wd1 <= wday && wday <= wd2;\n" 148 | " }\n" 149 | 150 | " return wd2 >= wday || wday >= wd1;\n" 151 | "}\n" 152 | 153 | "function dateRange() {\n" 154 | " function getMonth(name) {\n" 155 | " if (name in months) {\n" 156 | " return months[name];\n" 157 | " }\n" 158 | " return -1;\n" 159 | " }\n" 160 | " var date = new Date();\n" 161 | " var argc = arguments.length;\n" 162 | " if (argc < 1) {\n" 163 | " return false;\n" 164 | " }\n" 165 | " var isGMT = arguments[argc - 1] == \"GMT\";\n" 166 | 167 | " if (isGMT) {\n" 168 | " argc--;\n" 169 | " }\n" 170 | " // function will work even without explict handling of this case\n" 171 | " if (argc == 1) {\n" 172 | " var tmp = parseInt(arguments[0]);\n" 173 | " if (isNaN(tmp)) {\n" 174 | " return (\n" 175 | " (isGMT ? date.getUTCMonth() : date.getMonth()) == getMonth(arguments[0])\n" 176 | " );\n" 177 | " } else if (tmp < 32) {\n" 178 | " return (isGMT ? date.getUTCDate() : date.getDate()) == tmp;\n" 179 | " }\n" 180 | " return (isGMT ? date.getUTCFullYear() : date.getFullYear()) == tmp;\n" 181 | " }\n" 182 | " var year = date.getFullYear();\n" 183 | " var date1, date2;\n" 184 | " date1 = new Date(year, 0, 1, 0, 0, 0);\n" 185 | " date2 = new Date(year, 11, 31, 23, 59, 59);\n" 186 | " var adjustMonth = false;\n" 187 | " for (var i = 0; i < argc >> 1; i++) {\n" 188 | " var tmp = parseInt(arguments[i]);\n" 189 | " if (isNaN(tmp)) {\n" 190 | " var mon = getMonth(arguments[i]);\n" 191 | " date1.setMonth(mon);\n" 192 | " } else if (tmp < 32) {\n" 193 | " adjustMonth = argc <= 2;\n" 194 | " date1.setDate(tmp);\n" 195 | " } else {\n" 196 | " date1.setFullYear(tmp);\n" 197 | " }\n" 198 | " }\n" 199 | " for (var i = argc >> 1; i < argc; i++) {\n" 200 | " var tmp = parseInt(arguments[i]);\n" 201 | " if (isNaN(tmp)) {\n" 202 | " var mon = getMonth(arguments[i]);\n" 203 | " date2.setMonth(mon);\n" 204 | " } else if (tmp < 32) {\n" 205 | " date2.setDate(tmp);\n" 206 | " } else {\n" 207 | " date2.setFullYear(tmp);\n" 208 | " }\n" 209 | " }\n" 210 | " if (adjustMonth) {\n" 211 | " date1.setMonth(date.getMonth());\n" 212 | " date2.setMonth(date.getMonth());\n" 213 | " }\n" 214 | " if (isGMT) {\n" 215 | " var tmp = date;\n" 216 | " tmp.setFullYear(date.getUTCFullYear());\n" 217 | " tmp.setMonth(date.getUTCMonth());\n" 218 | " tmp.setDate(date.getUTCDate());\n" 219 | " tmp.setHours(date.getUTCHours());\n" 220 | " tmp.setMinutes(date.getUTCMinutes());\n" 221 | " tmp.setSeconds(date.getUTCSeconds());\n" 222 | " date = tmp;\n" 223 | " }\n" 224 | " return date1 <= date2\n" 225 | " ? date1 <= date && date <= date2\n" 226 | " : date2 >= date || date >= date1;\n" 227 | "}\n" 228 | 229 | "function timeRange() {\n" 230 | " var argc = arguments.length;\n" 231 | " var date = new Date();\n" 232 | " var isGMT = false;\n" 233 | " if (argc < 1) {\n" 234 | " return false;\n" 235 | " }\n" 236 | " if (arguments[argc - 1] == \"GMT\") {\n" 237 | " isGMT = true;\n" 238 | " argc--;\n" 239 | " }\n" 240 | 241 | " var hour = isGMT ? date.getUTCHours() : date.getHours();\n" 242 | " var date1, date2;\n" 243 | " date1 = new Date();\n" 244 | " date2 = new Date();\n" 245 | 246 | " if (argc == 1) {\n" 247 | " return hour == arguments[0];\n" 248 | " } else if (argc == 2) {\n" 249 | " return arguments[0] <= hour && hour <= arguments[1];\n" 250 | " }\n" 251 | " switch (argc) {\n" 252 | " case 6:\n" 253 | " date1.setSeconds(arguments[2]);\n" 254 | " date2.setSeconds(arguments[5]);\n" 255 | " // falls through\n" 256 | " case 4:\n" 257 | " var middle = argc >> 1;\n" 258 | " date1.setHours(arguments[0]);\n" 259 | " date1.setMinutes(arguments[1]);\n" 260 | " date2.setHours(arguments[middle]);\n" 261 | " date2.setMinutes(arguments[middle + 1]);\n" 262 | " if (middle == 2) {\n" 263 | " date2.setSeconds(59);\n" 264 | " }\n" 265 | " break;\n" 266 | " default:\n" 267 | " throw new Error(\"timeRange: bad number of arguments\");\n" 268 | " }\n" 269 | 270 | " if (isGMT) {\n" 271 | " date.setFullYear(date.getUTCFullYear());\n" 272 | " date.setMonth(date.getUTCMonth());\n" 273 | " date.setDate(date.getUTCDate());\n" 274 | " date.setHours(date.getUTCHours());\n" 275 | " date.setMinutes(date.getUTCMinutes());\n" 276 | " date.setSeconds(date.getUTCSeconds());\n" 277 | " }\n" 278 | " return date1 <= date2\n" 279 | " ? date1 <= date && date <= date2\n" 280 | " : date2 >= date || date >= date1;\n" 281 | "}\n" 282 | ; 283 | 284 | -------------------------------------------------------------------------------- /sspi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * These are SSPI authentication routines for the NTLM module of CNTLM 3 | * Used only on Win32/64 (Cygwin) 4 | * 5 | * CNTLM is free software; you can redistribute it and/or modify it under the 6 | * terms of the GNU General Public License as published by the Free Software 7 | * Foundation; either version 2 of the License, or (at your option) any later 8 | * version. 9 | * 10 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 11 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 13 | * details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 17 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 18 | * 19 | * Copyright (c) 2013 Denis Galkin aka Evengard, David Kubicek 20 | * 21 | */ 22 | 23 | #ifdef __CYGWIN__ 24 | 25 | #include "sspi.h" 26 | #include "utils.h" 27 | 28 | // SSPI mode 29 | #ifdef UNICODE 30 | wchar_t* sspi_mode = NULL; 31 | #else 32 | char* sspi_mode = NULL; 33 | #endif 34 | 35 | // Security DLL handle 36 | HMODULE sspi_dll = NULL; 37 | 38 | // Function pointers 39 | ACCEPT_SECURITY_CONTEXT_FN _AcceptSecurityContext = NULL; 40 | ACQUIRE_CREDENTIALS_HANDLE_FN _AcquireCredentialsHandle = NULL; 41 | COMPLETE_AUTH_TOKEN_FN _CompleteAuthToken = NULL; 42 | DELETE_SECURITY_CONTEXT_FN _DeleteSecurityContext = NULL; 43 | FREE_CONTEXT_BUFFER_FN _FreeContextBuffer = NULL; 44 | FREE_CREDENTIALS_HANDLE_FN _FreeCredentialsHandle = NULL; 45 | INITIALIZE_SECURITY_CONTEXT_FN _InitializeSecurityContext = NULL; 46 | QUERY_SECURITY_PACKAGE_INFO_FN _QuerySecurityPackageInfo = NULL; 47 | QUERY_SECURITY_CONTEXT_TOKEN_FN _QuerySecurityContextToken = NULL; 48 | 49 | void UnloadSecurityDll(HMODULE hModule) { 50 | 51 | if (hModule) 52 | FreeLibrary(hModule); 53 | 54 | _AcceptSecurityContext = NULL; 55 | _AcquireCredentialsHandle = NULL; 56 | _CompleteAuthToken = NULL; 57 | _DeleteSecurityContext = NULL; 58 | _FreeContextBuffer = NULL; 59 | _FreeCredentialsHandle = NULL; 60 | _InitializeSecurityContext = NULL; 61 | _QuerySecurityPackageInfo = NULL; 62 | _QuerySecurityContextToken = NULL; 63 | } 64 | 65 | HMODULE LoadSecurityDll(void) { 66 | 67 | HMODULE hModule; 68 | BOOL fAllFunctionsLoaded = FALSE; 69 | TCHAR lpszDLL[MAX_PATH]; 70 | OSVERSIONINFO VerInfo; 71 | 72 | // 73 | // Find out which security DLL to use, depending on 74 | // whether we are on Windows NT or Windows 95, Windows 2000, Windows XP, or Windows Server 2003 75 | // We have to use security.dll on Windows NT 4.0. 76 | // All other operating systems, we have to use Secur32.dll 77 | // 78 | VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); 79 | if (!GetVersionEx (&VerInfo)) // If this fails, something has gone wrong 80 | { 81 | return FALSE; 82 | } 83 | 84 | if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && 85 | VerInfo.dwMajorVersion == 4 && 86 | VerInfo.dwMinorVersion == 0) 87 | { 88 | lstrcpy (lpszDLL, TEXT("security.dll")); 89 | } 90 | else 91 | { 92 | lstrcpy (lpszDLL, TEXT("secur32.dll")); 93 | } 94 | 95 | 96 | hModule = LoadLibrary(lpszDLL); 97 | if (!hModule) 98 | return NULL; 99 | 100 | #pragma GCC diagnostic push 101 | #pragma GCC diagnostic ignored "-Wcast-function-type" 102 | 103 | do { 104 | _AcceptSecurityContext = (ACCEPT_SECURITY_CONTEXT_FN) 105 | GetProcAddress(hModule, "AcceptSecurityContext"); 106 | if (!_AcceptSecurityContext) 107 | break; 108 | 109 | #ifdef UNICODE 110 | _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN) 111 | GetProcAddress(hModule, "AcquireCredentialsHandleW"); 112 | #else 113 | _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN) 114 | GetProcAddress(hModule, "AcquireCredentialsHandleA"); 115 | #endif 116 | if (!_AcquireCredentialsHandle) 117 | break; 118 | 119 | // CompleteAuthToken is not present on Windows 9x Secur32.dll 120 | // Do not check for the availability of the function if it is NULL; 121 | _CompleteAuthToken = (COMPLETE_AUTH_TOKEN_FN) 122 | GetProcAddress(hModule, "CompleteAuthToken"); 123 | 124 | _DeleteSecurityContext = (DELETE_SECURITY_CONTEXT_FN) 125 | GetProcAddress(hModule, "DeleteSecurityContext"); 126 | if (!_DeleteSecurityContext) 127 | break; 128 | 129 | _FreeContextBuffer = (FREE_CONTEXT_BUFFER_FN) 130 | GetProcAddress(hModule, "FreeContextBuffer"); 131 | if (!_FreeContextBuffer) 132 | break; 133 | 134 | _FreeCredentialsHandle = (FREE_CREDENTIALS_HANDLE_FN) 135 | GetProcAddress(hModule, "FreeCredentialsHandle"); 136 | if (!_FreeCredentialsHandle) 137 | break; 138 | 139 | #ifdef UNICODE 140 | _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN) 141 | GetProcAddress(hModule, "InitializeSecurityContextW"); 142 | #else 143 | _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN) 144 | GetProcAddress(hModule, "InitializeSecurityContextA"); 145 | #endif 146 | if (!_InitializeSecurityContext) 147 | break; 148 | 149 | #ifdef UNICODE 150 | _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN) 151 | GetProcAddress(hModule, "QuerySecurityPackageInfoW"); 152 | #else 153 | _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN) 154 | GetProcAddress(hModule, "QuerySecurityPackageInfoA"); 155 | #endif 156 | if (!_QuerySecurityPackageInfo) 157 | break; 158 | 159 | _QuerySecurityContextToken = (QUERY_SECURITY_CONTEXT_TOKEN_FN) 160 | GetProcAddress(hModule, "QuerySecurityContextToken"); 161 | if (!_QuerySecurityContextToken) 162 | break; 163 | 164 | fAllFunctionsLoaded = TRUE; 165 | 166 | } while (NULL); 167 | 168 | #pragma GCC diagnostic pop 169 | 170 | if (!fAllFunctionsLoaded) { 171 | UnloadSecurityDll(hModule); 172 | hModule = NULL; 173 | } 174 | 175 | return hModule; 176 | } 177 | 178 | int sspi_enabled(void) 179 | { 180 | if (sspi_mode != NULL) 181 | return 1; 182 | return 0; 183 | } 184 | 185 | int sspi_set(char* mode) 186 | { 187 | sspi_dll = LoadSecurityDll(); 188 | if (!strcasecmp("NTLM", mode) && sspi_dll) // Only NTLM supported for now 189 | { 190 | #ifdef UNICODE 191 | sspi_mode = zmalloc(sizeof(wchar_t) * strlen(mode)); 192 | mbstowcs(sspi_mode, mode, strlen(mode)); 193 | #else 194 | sspi_mode = strdup(mode); 195 | #endif 196 | return 1; 197 | } 198 | sspi_mode = NULL; 199 | return 0; 200 | } 201 | 202 | int sspi_unset(void) 203 | { 204 | free(sspi_mode); 205 | sspi_mode = NULL; 206 | UnloadSecurityDll(sspi_dll); 207 | sspi_dll = NULL; 208 | return 1; 209 | } 210 | 211 | int sspi_request(char **dst, struct sspi_handle *sspi) 212 | { 213 | SECURITY_STATUS status; 214 | TimeStamp expiry; 215 | 216 | status = _AcquireCredentialsHandle( 217 | NULL, // Use current principal 218 | sspi_mode, 219 | SECPKG_CRED_OUTBOUND, 220 | NULL, 221 | NULL, 222 | NULL, 223 | NULL, 224 | &sspi->credentials, 225 | &expiry); 226 | 227 | if (status != SEC_E_OK) 228 | return 0; 229 | 230 | SecBufferDesc tokenDesc; 231 | SecBuffer token; 232 | unsigned int attrs; 233 | 234 | tokenDesc.ulVersion = SECBUFFER_VERSION; 235 | tokenDesc.cBuffers = 1; 236 | tokenDesc.pBuffers = &token; 237 | token.cbBuffer = TOKEN_BUFSIZE; 238 | token.BufferType = SECBUFFER_TOKEN; 239 | token.pvBuffer = zmalloc(TOKEN_BUFSIZE); 240 | 241 | status = _InitializeSecurityContext( 242 | &sspi->credentials, 243 | NULL, 244 | TEXT(""), 245 | ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION, 246 | 0, 247 | SECURITY_NETWORK_DREP, 248 | NULL, 249 | 0, 250 | &sspi->context, 251 | &tokenDesc, 252 | &attrs, 253 | &expiry); 254 | 255 | if(status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) 256 | _CompleteAuthToken(&sspi->context, &tokenDesc); 257 | else if(status != SEC_E_OK) 258 | { 259 | _FreeCredentialsHandle(&sspi->context); 260 | return 0; 261 | } 262 | 263 | *dst = token.pvBuffer; 264 | return token.cbBuffer; 265 | } 266 | 267 | int sspi_response(char **dst, char *challengeBuf, int challen, struct sspi_handle *sspi) 268 | { 269 | SecBuffer challenge; 270 | SecBuffer answer; 271 | SecBufferDesc challengeDesc; 272 | SecBufferDesc answerDesc; 273 | SECURITY_STATUS status; 274 | unsigned int attrs; 275 | TimeStamp expiry; 276 | 277 | challengeDesc.ulVersion = answerDesc.ulVersion = SECBUFFER_VERSION; 278 | challengeDesc.cBuffers = answerDesc.cBuffers = 1; 279 | 280 | challengeDesc.pBuffers = &challenge; 281 | answerDesc.pBuffers = &answer; 282 | 283 | challenge.BufferType = answer.BufferType = SECBUFFER_TOKEN; 284 | 285 | challenge.pvBuffer = challengeBuf; 286 | challenge.cbBuffer = challen; 287 | answer.pvBuffer = zmalloc(TOKEN_BUFSIZE); 288 | answer.cbBuffer = TOKEN_BUFSIZE; 289 | 290 | status = _InitializeSecurityContext( 291 | &sspi->credentials, 292 | &sspi->context, 293 | TEXT(""), 294 | ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION, 295 | 0, 296 | SECURITY_NETWORK_DREP, 297 | &challengeDesc, 298 | 0, 299 | &sspi->context, 300 | &answerDesc, 301 | &attrs, 302 | &expiry); 303 | 304 | if(status != SEC_E_OK) 305 | return 0; 306 | 307 | *dst = answer.pvBuffer; 308 | return answer.cbBuffer; 309 | } 310 | 311 | #endif /* __CYGWIN__ */ 312 | -------------------------------------------------------------------------------- /rpm/cntlm.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # cntlmd: Start/stop the cntlm proxy. 4 | # 5 | # chkconfig: 2345 26 89 6 | # Description: Cntlm is meant to be given your proxy address and becomming 7 | # the primary proxy then, listening on a selected local port. 8 | # You point all your proxy-aware programs to it and don't ever 9 | # have to deal with proxy authentication again. 10 | # 11 | # Authors: Radislav Vrnata 12 | # Michal Strnad 13 | # Christian Wittmer 14 | # 15 | ### BEGIN INIT INFO 16 | # Provides: cntlm 17 | # Required-Start: $syslog $network $time 18 | # Should-Start: $remote_fs 19 | # Required-Stop: $syslog $network $time 20 | # Should-Stop: $remote_fs 21 | # Default-Start: 2 3 5 22 | # Default-Stop: 0 1 6 23 | # Short-Description: start/stop the cntlm proxy 24 | # Description: ntlm is meant to be given your proxy address and becomming 25 | # the primary proxy then, listening on a selected local port. 26 | # You point all your proxy-aware programs to it and don't ever 27 | # have to deal with proxy authentication again. 28 | ### END INIT INFO 29 | # 30 | # Note on runlevels: 31 | # 0 - halt/poweroff 6 - reboot 32 | # 1 - single user 33 | # 2 - multiuser without network exported 34 | # 3 - multiuser with network (text mode) 35 | # 4 - Not used/User-definable 36 | # 5 - multiuser with network and X11 (xdm) 37 | # 6 - reboot 38 | # 39 | 40 | # Determining Linux RedHat/SuSE 41 | # 42 | # /etc/redhat-release 43 | # /etc/SuSE-release 44 | 45 | SuSE=false 46 | RedHat=false 47 | 48 | if [ -f /etc/SuSE-release ]; then 49 | SuSE=true 50 | elif [ -f /etc/redhat-release ]; then 51 | RedHat=true 52 | else 53 | echo "Error: your platform is not supported by $0" > /dev/stderr 54 | exit 1 55 | fi 56 | 57 | 58 | # Source function library SuSE/RedHat. 59 | if $SuSE; then 60 | # Source LSB init functions 61 | # providing start_daemon, killproc, pidofproc, 62 | # log_success_msg, log_failure_msg and log_warning_msg. 63 | # This is currently not used by UnitedLinux based distributions and 64 | # not needed for init scripts for UnitedLinux only. If it is used, 65 | # the functions from rc.status should not be sourced or used. 66 | #. /lib/lsb/init-functions 67 | 68 | # Shell functions sourced from /etc/rc.status: 69 | # rc_check check and set local and overall rc status 70 | # rc_status check and set local and overall rc status 71 | # rc_status -v be verbose in local rc status and clear it afterwards 72 | # rc_status -v -r ditto and clear both the local and overall rc status 73 | # rc_status -s display "skipped" and exit with status 3 74 | # rc_status -u display "unused" and exit with status 3 75 | # rc_failed set local and overall rc status to failed 76 | # rc_failed set local and overall rc status to 77 | # rc_reset clear both the local and overall rc status 78 | # rc_exit exit appropriate to overall rc status 79 | # rc_active checks whether a service is activated by symlinks 80 | 81 | # Return values acc. to LSB for all commands but status: 82 | # 0 - success 83 | # 1 - generic or unspecified error 84 | # 2 - invalid or excess argument(s) 85 | # 3 - unimplemented feature (e.g. "reload") 86 | # 4 - user had insufficient privileges 87 | # 5 - program is not installed 88 | # 6 - program is not configured 89 | # 7 - program is not running 90 | # 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) 91 | # 92 | # Note that starting an already running service, stopping 93 | # or restarting a not-running service as well as the restart 94 | # with force-reload (in case signaling is not supported) are 95 | # considered a success. 96 | test -f /etc/rc.status && . /etc/rc.status || { 97 | echo "Error: your platform is not supported by $0" > /dev/stderr; 98 | exit 1 99 | } 100 | rc_reset 101 | else 102 | test -f /etc/init.d/functions && . /etc/init.d/functions || { 103 | echo "Error: your platform is not supported by $0" > /dev/stderr; 104 | exit 1 105 | } 106 | RETVAL=0 107 | fi 108 | 109 | # Check for existence of needed config file and read it 110 | CNTLM_CONFIG="/etc/cntlm.conf" 111 | test -r $CNTLM_CONFIG || { echo "$CNTLM_CONFIG not existing"; 112 | if [ "$1" = "stop" ]; then exit 0; 113 | else exit 6; fi; } 114 | 115 | # Check for existence of needed sysconfig file and read it 116 | if $SuSE ; then 117 | CNTLM_SYSCONFIG="/etc/sysconfig/cntlm" 118 | else 119 | CNTLM_SYSCONFIG="/etc/sysconfig/cntlmd" 120 | fi 121 | test -r $CNTLM_SYSCONFIG && . $CNTLM_SYSCONFIG || { 122 | echo "$CNTLM_SYSCONFIG not existing"; 123 | if [ "$1" = "stop" ]; then exit 0; 124 | else exit 6; fi; } 125 | 126 | # some defaults 127 | [ -z "${DAEMON}" ] && DAEMON=/usr/sbin/cntlm 128 | [ -z "${DESC}" ] && DESC="CNTLM Authentication Proxy" 129 | [ -z "${PIDFILE}" ] && PIDFILE="/var/run/cntlm/cntlmd.pid" 130 | if $SuSE ; then 131 | [ -z "${LOCKFILE}" ] && LOCKFILE="/var/lock/subsys/cntlm" 132 | else 133 | [ -z "${LOCKFILE}" ] && LOCKFILE="/var/lock/subsys/cntlmd" 134 | fi 135 | [ -z "${RUNAS}" ] && RUNAS="cntlm" 136 | 137 | # if no "Proxy" is set in cntlm.conf try '127.0.0.1:3128' as a default 138 | if [ `/bin/cat $CNTLM_CONFIG | grep -e "^Listen" >/dev/null; echo $?` -eq 0 ]; then 139 | CNTLM_LISTEN= 140 | else 141 | CNTLM_LISTEN="-l 127.0.0.1:3128" 142 | fi 143 | 144 | # Check for missing binaries (stale symlinks should not happen) 145 | # Note: Special treatment of stop for LSB conformance 146 | test -x $DAEMON || { echo "$DAEMON not installed"; 147 | if [ "$1" = "stop" ]; then exit 0; 148 | else exit 5; fi; } 149 | 150 | case "$1" in 151 | start) 152 | echo -n "Starting ${DESC}: " 153 | if $SuSE; then 154 | ## Start daemon with startproc(8). If this fails 155 | ## the return value is set appropriately by startproc. 156 | /sbin/startproc -p $PIDFILE $DAEMON -P $PIDFILE $CNTLM_LISTEN -U $RUNAS $OPTARGS &>/dev/null 157 | 158 | # Remember status and be verbose 159 | rc_status -v 160 | else 161 | daemon cntlm -P $PIDFILE $CNTLM_LISTEN -U $RUNAS $OPTARGS 2>/dev/null 162 | RETVAL=$? 163 | echo 164 | [ $RETVAL -eq 0 ] && touch $LOCKFILE 165 | exit $RETVAL 166 | fi 167 | ;; 168 | stop) 169 | echo -n "Shutting down ${DESC}: " 170 | if $SuSE; then 171 | ## Stop daemon with killproc(8) and if this fails 172 | ## killproc sets the return value according to LSB. 173 | /sbin/killproc -p $PIDFILE -TERM $DAEMON &>/dev/null 174 | 175 | # Remember status and be verbose 176 | rc_status -v 177 | else 178 | killproc cntlm 179 | RETVAL=$? 180 | echo 181 | [ $RETVAL -eq 0 ] && rm -f $LOCKFILE 182 | exit $RETVAL 183 | fi 184 | ;; 185 | try-restart|condrestart) 186 | ## Do a restart only if the service was active before. 187 | ## Note: try-restart is now part of LSB (as of 1.9). 188 | ## RH has a similar command named condrestart. 189 | if test "$1" = "condrestart"; then 190 | echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" 191 | fi 192 | $0 status 193 | if test $? = 0; then 194 | $0 restart 195 | else 196 | if $SuSE; then 197 | rc_reset # Not running is not a failure. 198 | # Remember status and be quiet 199 | rc_status 200 | else 201 | exit 0 202 | fi 203 | fi 204 | ;; 205 | restart) 206 | ## Stop the service and regardless of whether it was 207 | ## running or not, start it again. 208 | $0 stop 209 | $0 start 210 | 211 | if $SuSE; then 212 | # Remember status and be quiet 213 | rc_status 214 | fi 215 | ;; 216 | force-reload|reload) 217 | ## Signal the daemon to reload its config. Most daemons 218 | ## do this on signal 1 (SIGHUP). 219 | ## If it does not support it, restart the service if it 220 | ## is running. 221 | 222 | # cntlm does not support SIGHUP, so restart 223 | echo -n "Reload ${DESC}: " 224 | ## if it supports it: 225 | #/sbin/killproc -p $PIDFILE -HUP $DAEMON 226 | 227 | # Remember status and be verbose 228 | #rc_status -v 229 | 230 | ## Otherwise: 231 | $0 try-restart 232 | 233 | if $SuSE; then 234 | # Remember status and be quiet 235 | rc_status 236 | fi 237 | ;; 238 | status) 239 | echo -n "Checking for ${DESC}: " 240 | if $SuSE; then 241 | ## Check status with checkproc(8), if process is running 242 | ## checkproc will return with exit status 0. 243 | 244 | # Return value is slightly different for the status command: 245 | # 0 - service up and running 246 | # 1 - service dead, but /var/run/ pid file exists 247 | # 2 - service dead, but /var/lock/ lock file exists 248 | # 3 - service not running (unused) 249 | # 4 - service status unknown :-( 250 | # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) 251 | 252 | # NOTE: checkproc returns LSB compliant status values. 253 | /sbin/checkproc -p $PIDFILE $DAEMON 254 | # NOTE: rc_status knows that we called this init script with 255 | # "status" option and adapts its messages accordingly. 256 | 257 | # Remember status and be verbose 258 | rc_status -v 259 | else 260 | status cntlm 261 | fi 262 | ;; 263 | *) 264 | echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}" 265 | exit 1 266 | ;; 267 | esac 268 | if $SuSE; then 269 | rc_exit 270 | else 271 | exit $RETVAL 272 | fi 273 | -------------------------------------------------------------------------------- /scanner.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CNTLM is free software; you can redistribute it and/or modify it under the 3 | * terms of the GNU General Public License as published by the Free Software 4 | * Foundation; either version 2 of the License, or (at your option) any later 5 | * version. 6 | * 7 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | * details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 14 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 15 | * 16 | * Copyright (c) 2007 David Kubicek 17 | * 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "scanner.h" 29 | #include "socket.h" 30 | #include "http.h" 31 | #include "globals.h" 32 | #include "forward.h" 33 | #include "proxy.h" 34 | 35 | /* 36 | * This code is a piece of shit, but it works. Cannot rewrite it now, because 37 | * I don't have ISA AV filter anymore - wouldn't be able to test it. 38 | */ 39 | int scanner_hook(rr_data_const_t request, rr_data_t response, struct auth_s *credentials, int cd, int *sd, long maxKBs) { 40 | char *buf; 41 | char *line; 42 | char *tmp; 43 | char *pat; 44 | char *post; 45 | char *isaid; 46 | char *uurl; 47 | const char *pos; 48 | int bsize; 49 | int lsize; 50 | int size; 51 | int len; 52 | int i; 53 | int nc; 54 | rr_data_t newreq; 55 | rr_data_t newres; 56 | plist_t list; 57 | 58 | int ok = 1; 59 | int done = 0; 60 | int headers_initiated = 0; 61 | long c, progress = 0, filesize = 0; 62 | 63 | /* 64 | * Let's limit the responses we examine to an absolute minimum 65 | */ 66 | if (!request->req || response->code != 200 67 | || http_has_body(request, response) != -1 68 | || hlist_subcmp(response->headers, "Transfer-Encoding", "chunked") 69 | || !hlist_subcmp(response->headers, "Proxy-Connection", "close")) 70 | return PLUG_SENDHEAD | PLUG_SENDDATA; 71 | 72 | tmp = hlist_get(request->headers, "User-Agent"); 73 | if (tmp) { 74 | tmp = lowercase(strdup(tmp)); 75 | list = scanner_agent_list; 76 | while (list) { 77 | pat = lowercase(strdup(list->aux)); 78 | if (debug) 79 | printf("scanner_hook: matching U-A header (%s) to %s\n", tmp, pat); 80 | if (!fnmatch(pat, tmp, 0)) { 81 | if (debug) 82 | printf("scanner_hook: positive match!\n"); 83 | maxKBs = 0; 84 | free(pat); 85 | break; 86 | } 87 | free(pat); 88 | list = list->next; 89 | } 90 | free(tmp); 91 | } 92 | 93 | bsize = SAMPLE; 94 | buf = zmalloc(bsize); 95 | 96 | len = 0; 97 | do { 98 | size = read(*sd, buf + len, SAMPLE - len - 1); 99 | if (debug) 100 | printf("scanner_hook: read %d of %d\n", size, SAMPLE - len); 101 | if (size > 0) 102 | len += size; 103 | } while (size > 0 && len < SAMPLE - 1); 104 | 105 | if (strstr(buf, "Downloading status") && (pos=strstr(buf, "ISAServerUniqueID=")) && (pos = strchr(pos, '"'))) { 106 | pos++; 107 | c = strlen(pos); 108 | for (i = 0; i < c && pos[i] != '"'; ++i) {}; 109 | 110 | if (pos[i] == '"') { 111 | isaid = substr(pos, 0, i); 112 | if (debug) 113 | printf("scanner_hook: ISA id = %s\n", isaid); 114 | 115 | lsize = BUFSIZE; 116 | line = zmalloc(lsize); 117 | do { 118 | i = so_recvln(*sd, &line, &lsize); 119 | 120 | c = strlen(line); 121 | if (len + c >= bsize) { 122 | bsize *= 2; 123 | tmp = realloc(buf, bsize); 124 | if (tmp == NULL) 125 | break; 126 | else 127 | buf = tmp; 128 | } 129 | 130 | strlcat(buf, line, bsize); 131 | len += c; 132 | 133 | if (i >= 0) { 134 | int update_page = 0; 135 | int download_finished = 0; 136 | 137 | if ((pos = strstr(line, "UpdatePage(")) && isdigit((u_char)pos[11])) { 138 | update_page = 1; 139 | } 140 | else if ((pos = strstr(line, "DownloadFinished(")) && isdigit((u_char)pos[17])) { 141 | download_finished = 1; 142 | } 143 | 144 | if (update_page || download_finished) { 145 | if (download_finished) { 146 | done = 1; 147 | } 148 | 149 | if (debug) 150 | printf("scanner_hook: %s", line); 151 | 152 | if ((pos = strstr(line, "To be downloaded"))) { 153 | filesize = atol(pos+16); 154 | if (debug) { 155 | if (filesize > 0) { 156 | printf("scanner_hook: file size detected: %ld KiBs (max: %ld)\n", filesize/1024, maxKBs); 157 | } else { 158 | printf("scanner_hook: file size unknown -- quitting\n"); 159 | break; 160 | } 161 | } 162 | 163 | if (maxKBs && (maxKBs == 1 || filesize/1024 > maxKBs)) 164 | break; 165 | 166 | /* 167 | * We have to send HTTP protocol ID so we can send the notification 168 | * headers during downloading. Once we've done that, it cannot appear 169 | * again, which it would if we returned PLUG_SENDHEAD, so we must 170 | * remember to not include it. 171 | */ 172 | headers_initiated = 1; 173 | tmp = zmalloc(MINIBUF_SIZE); 174 | snprintf(tmp, MINIBUF_SIZE, "%s 200 OK\r\n", request->http); 175 | (void) write_wrapper(cd, tmp, strlen(tmp)); // We don't really care about the result 176 | free(tmp); 177 | } 178 | 179 | if (!headers_initiated) { 180 | if (debug) 181 | printf("scanner_hook: Giving up, \"To be downloaded\" line not found!\n"); 182 | break; 183 | } 184 | 185 | /* 186 | * Send a notification header to the client, just so it doesn't timeout 187 | */ 188 | if (!done) { 189 | tmp = zmalloc(MINIBUF_SIZE); 190 | progress = atol(line+12); 191 | snprintf(tmp, MINIBUF_SIZE, "ISA-Scanner: %ld of %ld\r\n", progress, filesize); 192 | (void) write_wrapper(cd, tmp, strlen(tmp)); 193 | free(tmp); 194 | } 195 | 196 | /* 197 | * If download size is unknown beforehand, stop when downloaded amount is over ISAScannerSize 198 | */ 199 | if (!filesize && maxKBs && maxKBs != 1 && progress/1024 > maxKBs) 200 | break; 201 | } 202 | } 203 | } while (i > 0 && !done); 204 | 205 | if (i >= 0 && done && (pos = strstr(line, "\",\"")+3) && (c = strchr(pos, '"') - pos) > 0) { 206 | char * encoded_url; 207 | 208 | tmp = substr(pos, 0, c); 209 | encoded_url = urlencode(tmp); 210 | free(tmp); 211 | 212 | uurl = urlencode(request->url); 213 | 214 | post = zmalloc(BUFSIZE); 215 | snprintf(post, BUFSIZE-1, "%surl=%s&%sSaveToDisk=YES&%sOrig=%s", isaid, encoded_url, isaid, isaid, uurl); 216 | free(encoded_url); 217 | 218 | if (debug) 219 | printf("scanner_hook: Getting file with URL data = %s\n", request->url); 220 | 221 | tmp = zmalloc(MINIBUF_SIZE); 222 | snprintf(tmp, MINIBUF_SIZE, "%d", (int)strlen(post)); 223 | 224 | newres = new_rr_data(); 225 | newreq = dup_rr_data(request); 226 | 227 | free(newreq->method); 228 | newreq->method = strdup("POST"); 229 | hlist_mod(newreq->headers, "Referer", request->url, 1); 230 | hlist_mod(newreq->headers, "Content-Type", "application/x-www-form-urlencoded", 1); 231 | hlist_mod(newreq->headers, "Content-Length", tmp, 1); 232 | free(tmp); 233 | 234 | nc = proxy_connect(credentials, newreq->url, newreq->hostname); 235 | c = proxy_authenticate(&nc, newreq, newres, credentials); 236 | if (c && newres->code == 407) { 237 | if (debug) 238 | printf("scanner_hook: Authentication OK, getting the file...\n"); 239 | } else { 240 | if (debug) 241 | printf("scanner_hook: Authentication failed or refused!\n"); 242 | close(nc); 243 | nc = 0; 244 | } 245 | 246 | /* 247 | * The POST request for the real file 248 | */ 249 | reset_rr_data(newres); 250 | if (nc && headers_send(nc, newreq) && write_wrapper(nc, post, strlen(post)) && headers_recv(nc, newres)) { 251 | if (debug) 252 | hlist_dump(newres->headers); 253 | 254 | /* 255 | * We always know the filesize here. Send it to the client, because ISA doesn't!!! 256 | * The clients progress bar doesn't work without it and it stinks! 257 | */ 258 | if (filesize || progress) { 259 | tmp = zmalloc(22); 260 | snprintf(tmp, 22, "%ld", filesize ? filesize : progress); 261 | newres->headers = hlist_mod(newres->headers, "Content-Length", tmp, 1); 262 | free(tmp); 263 | } 264 | 265 | /* 266 | * Here we remember if previous code already sent some headers 267 | * to the client. In such case, do not include the HTTP/1.x ID. 268 | */ 269 | newres->skip_http = headers_initiated; 270 | copy_rr_data(response, newres); 271 | close(*sd); 272 | *sd = nc; 273 | 274 | len = 0; 275 | ok = PLUG_SENDHEAD | PLUG_SENDDATA; 276 | } else if (debug) 277 | printf("scanner_hook: New request failed\n"); 278 | 279 | free_rr_data(&newreq); 280 | free_rr_data(&newres); 281 | free(post); 282 | free(uurl); 283 | } 284 | 285 | free(line); 286 | free(isaid); 287 | } else if (debug) 288 | printf("scanner_hook: ISA id not found\n"); 289 | } 290 | 291 | if (len) { 292 | if (debug) { 293 | printf("scanner_hook: flushing %d original bytes\n", len); 294 | hlist_dump(response->headers); 295 | } 296 | 297 | if (!headers_send(cd, response)) { 298 | if (debug) 299 | printf("scanner_hook: failed to send headers\n"); 300 | free(buf); 301 | return PLUG_ERROR; 302 | } 303 | 304 | size = write_wrapper(cd, buf, len); 305 | if (size > 0) 306 | ok = PLUG_SENDDATA; 307 | else 308 | ok = PLUG_ERROR; 309 | } 310 | 311 | if (debug) 312 | printf("scanner_hook: ending with %d\n", ok); 313 | 314 | free(buf); 315 | return ok; 316 | } 317 | 318 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # You can tweak these three variables to make things install where you 3 | # like, but do not touch more unless you know what you are doing. ;) 4 | # 5 | DESTDIR := 6 | PREFIX := /usr/local 7 | SYSCONFDIR := $(DESTDIR)/etc 8 | BINDIR := $(DESTDIR)$(PREFIX)/sbin 9 | MANDIR := $(DESTDIR)$(PREFIX)/share/man 10 | 11 | ifeq ($(wildcard configure-stamp),) 12 | _ := $(shell ./configure) 13 | endif 14 | 15 | # 16 | # Careful now... 17 | # __BSD_VISIBLE is for FreeBSD AF_* constants 18 | # _ALL_SOURCE is for AIX 5.3 LOG_PERROR constant 19 | # 20 | CC := $(shell head -n 1 configure-stamp) 21 | VER := $(shell cat VERSION) 22 | OS := $(shell uname -s) 23 | OSLDFLAGS := $(shell [ $(OS) = "SunOS" ] && echo "-lrt -lsocket -lnsl") 24 | LDFLAGS := -lpthread -lm $(OSLDFLAGS) 25 | CYGWIN_REQS := cygwin1.dll cygrunsrv.exe 26 | 27 | ifeq ($(OS),Darwin) 28 | ifndef ARCH 29 | ARCH := $(shell uname -m) 30 | endif 31 | CFLAGS := -arch $(ARCH) 32 | # Change binary directory for macOS 33 | BINDIR := $(DESTDIR)$(PREFIX)/bin 34 | endif 35 | 36 | ifeq ($(CC),gcc) 37 | GCC_VER := $(shell ${CC} -dumpfullversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') 38 | GCC_GTEQ_430 := $(shell expr ${GCC_VER} \>= 40300) 39 | GCC_GTEQ_450 := $(shell expr ${GCC_VER} \>= 40500) 40 | GCC_GTEQ_600 := $(shell expr ${GCC_VER} \>= 60000) 41 | GCC_GTEQ_700 := $(shell expr ${GCC_VER} \>= 70000) 42 | endif 43 | 44 | CFLAGS += -std=c99 -D__BSD_VISIBLE -D_ALL_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112 -D_ISOC99_SOURCE -D_REENTRANT -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_DARWIN_C_SOURCE -DVERSION=\"'$(VER)'\" 45 | CFLAGS += -Wall -Wextra -pedantic -Wshadow -Wcast-qual -Wbad-function-cast -Wstrict-prototypes -Wno-overlength-strings 46 | CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 47 | #CFLAGS += -ftrapv 48 | #CFLAGS += -fsanitize=undefined -fsanitize-undefined-trap-on-error 49 | 50 | ifeq ($(CC),gcc) 51 | ifeq "$(GCC_GTEQ_430)" "1" 52 | CFLAGS += -Wlogical-op 53 | endif 54 | ifeq "$(GCC_GTEQ_450)" "1" 55 | CFLAGS += -Wjump-misses-init 56 | endif 57 | ifeq "$(GCC_GTEQ_600)" "1" 58 | CFLAGS += -Wduplicated-cond 59 | CFLAGS += -Wnull-dereference 60 | CFLAGS += -Werror=uninitialized 61 | CFLAGS += -Wformat=2 62 | CFLAGS += -Wformat-overflow=2 63 | CFLAGS += -Wformat-truncation=2 64 | CFLAGS += -Wformat-security 65 | endif 66 | ifeq "$(GCC_GTEQ_700)" "1" 67 | CFLAGS += -Wduplicated-branches 68 | endif 69 | endif 70 | 71 | #CFLAGS += -fstack-protector-strong 72 | #CFLAGS += -v 73 | ifeq ($(COVERAGE),1) 74 | # COVERAGE REPORT 75 | CFLAGS += -g --coverage 76 | else ifeq ($(DEBUG),1) 77 | # DEBUG 78 | CFLAGS += -g -O0 79 | else 80 | # RELEASE 81 | CFLAGS += -O3 82 | endif 83 | 84 | OBJS=main.o utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o proxy.o pac.o duktape.o 85 | 86 | CONFIG_GSS=$(shell grep -c "config_gss 1" config/config.h) 87 | ifeq ($(CONFIG_GSS),1) 88 | OBJS+=kerberos.o 89 | ifeq ($(OS),Darwin) 90 | LDFLAGS+=-framework GSS 91 | else 92 | LDFLAGS+=-lgssapi_krb5 93 | endif 94 | endif 95 | 96 | ifneq ($(findstring CYGWIN,$(OS)),) 97 | OBJS+=sspi.o win/resources.o 98 | endif 99 | 100 | # Static linking is not available on macOS 101 | ENABLE_STATIC=$(shell grep -c ENABLE_STATIC configure-stamp) 102 | ifeq ($(ENABLE_STATIC),1) 103 | ifneq ($(OS),Darwin) 104 | LDFLAGS+=-static 105 | endif 106 | endif 107 | 108 | CFLAGS_DUKTAPE := -Wno-bad-function-cast -Wno-null-dereference -Wno-format-nonliteral -Wno-unused-but-set-variable 109 | ifeq ($(CC),gcc) 110 | CFLAGS_DUKTAPE += -Wno-format-overflow 111 | endif 112 | 113 | all: cntlm 114 | 115 | cntlm: $(OBJS) 116 | @echo "Linking $@" 117 | @$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) 118 | 119 | main.o: main.c *.h config/config.h 120 | @echo "Compiling $<" 121 | @if [ -z "$(SYSCONFDIR)" ]; then \ 122 | $(CC) $(CFLAGS) -c main.c -o $@; \ 123 | else \ 124 | $(CC) $(CFLAGS) -DSYSCONFDIR=\"$(SYSCONFDIR)\" -c main.c -o $@; \ 125 | fi 126 | 127 | %.o: %.c *.h config/config.h 128 | @echo "Compiling $<" 129 | @$(CC) $(CFLAGS) -c -o $@ $< 130 | 131 | duktape.o: duktape/duktape.c 132 | @echo "Compiling $<" 133 | @$(CC) $(CFLAGS) $(CFLAGS_DUKTAPE) -c -o $@ $< 134 | 135 | win/resources.o: win/resources.rc 136 | @echo Win64: adding ICON resource 137 | @windres $^ -o $@ 138 | 139 | install: cntlm 140 | # Special handling for install(1) 141 | @if [ "`uname -s`" = "AIX" ]; then \ 142 | install -M 755 -S -f $(BINDIR) cntlm; \ 143 | install -M 644 -f $(MANDIR)/man1 doc/cntlm.1; \ 144 | install -M 600 -c $(SYSCONFDIR) doc/cntlm.conf; \ 145 | elif [ "`uname -s`" = "Darwin" ]; then \ 146 | install -d $(BINDIR)/; \ 147 | install -m 755 -s cntlm $(BINDIR)/cntlm; \ 148 | install -d $(MANDIR)/man1/; \ 149 | install -m 644 doc/cntlm.1 $(MANDIR)/man1/cntlm.1; \ 150 | [ -f $(SYSCONFDIR)/cntlm.conf -o -z "$(SYSCONFDIR)" ] \ 151 | || install -d $(SYSCONFDIR)/; \ 152 | install -m 600 doc/cntlm.conf $(SYSCONFDIR)/cntlm.conf; \ 153 | else \ 154 | install -D -m 755 -s cntlm $(BINDIR)/cntlm; \ 155 | install -D -m 644 doc/cntlm.1 $(MANDIR)/man1/cntlm.1; \ 156 | [ -f $(SYSCONFDIR)/cntlm.conf -o -z "$(SYSCONFDIR)" ] \ 157 | || install -D -m 600 doc/cntlm.conf $(SYSCONFDIR)/cntlm.conf; \ 158 | fi 159 | @echo; echo "Cntlm will look for configuration in $(SYSCONFDIR)/cntlm.conf" 160 | 161 | tgz: 162 | @mkdir -p tmp 163 | @rm -rf tmp/cntlm-$(VER) 164 | @git checkout-index -a -f --prefix=./tmp/cntlm-$(VER)/ 165 | @tar zcvf cntlm-$(VER).tar.gz -C tmp/ cntlm-$(VER) 166 | @rm -rf tmp/cntlm-$(VER) 167 | @rmdir tmp 2>/dev/null || true 168 | 169 | tbz2: 170 | @mkdir -p tmp 171 | @rm -rf tmp/cntlm-$(VER) 172 | @git checkout-index -a -f --prefix=./tmp/cntlm-$(VER)/ 173 | @tar jcvf cntlm-$(VER).tar.bz2 -C tmp/ cntlm-$(VER) 174 | @rm -rf tmp/cntlm-$(VER) 175 | @rmdir tmp 2>/dev/null || true 176 | 177 | deb: 178 | @sed -i "s/^\(cntlm *\)([^)]*)/\1($(VER))/g" debian/changelog 179 | @if [ `id -u` = 0 ]; then \ 180 | debian/rules binary; \ 181 | debian/rules clean; \ 182 | else \ 183 | fakeroot debian/rules binary; \ 184 | fakeroot debian/rules clean; \ 185 | fi 186 | @mv ../cntlm_$(VER)*.deb . 187 | 188 | rpm: 189 | @sed -i "s/^\(Version:[\t ]*\)\(.*\)/\1$(VER)/g" rpm/cntlm.spec 190 | @if [ `id -u` = 0 ]; then \ 191 | rpm/rules binary; \ 192 | rpm/rules clean; \ 193 | else \ 194 | fakeroot rpm/rules binary; \ 195 | fakeroot rpm/rules clean; \ 196 | fi 197 | 198 | mac: cntlm cntlm-$(VER)-macos-$(ARCH).zip cntlm-$(VER)-macos-$(ARCH).pkg 199 | 200 | cntlm-$(VER)-macos-$(ARCH).zip: 201 | @echo macOS: creating ZIP release for manual installs 202 | @mkdir -p cntlm-$(VER) 203 | ifeq ($(DEBUG),1) 204 | @cp cntlm cntlm-$(VER)/cntlm 205 | else 206 | @strip -o cntlm-$(VER)/cntlm cntlm 207 | endif 208 | @cp doc/cntlm.conf doc/cntlm.1 LICENSE cntlm-$(VER)/ 209 | @zip -9 $@ cntlm-$(VER)/* 210 | @rm -rf cntlm-$(VER) 211 | @echo "Created $@" 212 | 213 | cntlm-$(VER)-macos-$(ARCH).pkg: 214 | @echo macOS: preparing binaries for PKG installer 215 | @rm -rf pkgroot 216 | @mkdir -p pkgroot/$(BINDIR) 217 | ifeq ($(DEBUG),1) 218 | @cp cntlm pkgroot/$(BINDIR)/cntlm 219 | else 220 | @strip -o pkgroot/$(BINDIR)/cntlm cntlm 221 | endif 222 | @chmod 755 pkgroot/$(BINDIR)/cntlm 223 | @mkdir -p pkgroot/$(SYSCONFDIR) 224 | @cp doc/cntlm.conf pkgroot/$(SYSCONFDIR)/cntlm.conf 225 | @chmod 600 pkgroot/$(SYSCONFDIR)/cntlm.conf 226 | @mkdir -p pkgroot/$(MANDIR)/man1 227 | @cp doc/cntlm.1 pkgroot/$(MANDIR)/man1/cntlm.1 228 | @chmod 644 pkgroot/$(MANDIR)/man1/cntlm.1 229 | @pkgbuild --root pkgroot --identifier com.cntlm.proxy.$(ARCH) --version $(VER) --install-location / pkgbuild-cntlm-$(ARCH).pkg 230 | @echo '' > distribution.xml 231 | @echo '' >> distribution.xml 232 | @echo 'cntlm $(VER) ($(ARCH))' >> distribution.xml 233 | @echo '' >> distribution.xml 234 | @echo '' >> distribution.xml 235 | @echo '' >> distribution.xml 236 | @echo '' >> distribution.xml 237 | @echo 'pkgbuild-cntlm-$(ARCH).pkg' >> distribution.xml 238 | @echo '' >> distribution.xml 239 | @productbuild --distribution distribution.xml --package-path . --resources . --version $(VER) cntlm-$(VER)-macos-$(ARCH).pkg 240 | @rm -rf pkgroot pkgbuild-cntlm-$(ARCH).pkg distribution.xml 241 | @echo "Created $@" 242 | 243 | win: win/setup.iss cntlm win/cntlm_manual.pdf win/cntlm.ini win/LICENSE.txt cntlm-$(VER)-win64.exe cntlm-$(VER)-win64.zip 244 | 245 | cntlm-$(VER)-win64.exe: 246 | @echo Win64: preparing binaries for GUI installer 247 | @cp $(patsubst %, /bin/%, $(CYGWIN_REQS)) win/ 248 | ifeq ($(DEBUG),1) 249 | @echo Win64: copy DEBUG executable 250 | @cp -p cntlm.exe win/ 251 | else 252 | @echo Win64: copy release executable 253 | @strip -o win/cntlm.exe cntlm.exe 254 | endif 255 | @echo Win64: generating GUI installer 256 | @win/Inno5/ISCC.exe /Q win/setup.iss #/Q win/setup.iss 257 | @echo "Created $@" 258 | 259 | cntlm-$(VER)-win64.zip: 260 | @echo Win64: creating ZIP release for manual installs 261 | @ln -s win cntlm-$(VER) 262 | @zip -9 $@ $(patsubst %, cntlm-$(VER)/%, cntlm.exe $(CYGWIN_REQS) cntlm.ini LICENSE.txt cntlm_manual.pdf) 263 | @rm -f cntlm-$(VER) 264 | @echo "Created $@" 265 | 266 | win/cntlm.ini: doc/cntlm.conf 267 | @cat doc/cntlm.conf | unix2dos > $@ 268 | 269 | win/LICENSE.txt: COPYRIGHT LICENSE 270 | @cat COPYRIGHT LICENSE | unix2dos > $@ 271 | 272 | win/cntlm_manual.pdf: doc/cntlm.1 273 | @echo Win64: generating PDF manual 274 | @rm -f $@ 275 | @groff -t -e -mandoc -Tps doc/cntlm.1 | ps2pdf - $@ 276 | 277 | win/setup.iss: win/setup.iss.in 278 | ifeq ($(findstring CYGWIN,$(OS)),) 279 | @echo 280 | @echo "* This build target must be run from a Cywgin shell on Windows *" 281 | @echo 282 | @exit 1 283 | endif 284 | @sed "s/\$$VERSION/$(VER)/g" $^ > $@ 285 | 286 | uninstall: 287 | @rm -f $(BINDIR)/cntlm $(MANDIR)/man1/cntlm.1 $(SYSCONFDIR)/cntlm.conf 2>/dev/null || true 288 | 289 | clean: 290 | @rm -f $(patsubst %, config/%, endian gethostname socklen_t strdup arc4random_buf strlcat strlcpy memset_s gss) config/*.exe 291 | @rm -f *.o cntlm cntlm.exe configure-stamp config/config.h 292 | @rm -f $(patsubst %, win/%, $(CYGWIN_REQS) cntlm.exe cntlm.ini LICENSE.txt resources.o setup.iss cntlm_manual.pdf) 293 | 294 | distclean: clean 295 | ifeq ($(OS),Linux) 296 | @if [ `id -u` = 0 ]; then \ 297 | debian/rules clean; \ 298 | rpm/rules clean; \ 299 | else \ 300 | fakeroot debian/rules clean; \ 301 | fakeroot rpm/rules clean; \ 302 | fi 303 | endif 304 | @rm -f *.exe *.deb *.rpm *.tgz *.tar.gz *.tar.bz2 *.zip *.exe *.pkg tags ctags pid 2>/dev/null 305 | 306 | .PHONY: all install tgz tbz2 deb rpm win uninstall clean distclean 307 | -------------------------------------------------------------------------------- /kerberos.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CNTLM is free software; you can redistribute it and/or modify it under the 3 | * terms of the GNU General Public License as published by the Free Software 4 | * Foundation; either version 2 of the License, or (at your option) any later 5 | * version. 6 | * 7 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | * details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 14 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 15 | * 16 | * Copyright (c) 2007 David Kubicek 17 | * 18 | */ 19 | 20 | //http://docs.oracle.com/cd/E18752_01/html/816-4863/sampleprogs-1.html 21 | /* 22 | * Copyright 1994 by OpenVision Technologies, Inc. 23 | * 24 | * Permission to use, copy, modify, distribute, and sell this software 25 | * and its documentation for any purpose is hereby granted without fee, 26 | * provided that the above copyright notice appears in all copies and 27 | * that both that copyright notice and this permission notice appear in 28 | * supporting documentation, and that the name of OpenVision not be used 29 | * in advertising or publicity pertaining to distribution of the software 30 | * without specific, written prior permission. OpenVision makes no 31 | * representations about the suitability of this software for any 32 | * purpose. It is provided "as is" without express or implied warranty. 33 | * 34 | * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 35 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 36 | * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR 37 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 38 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 39 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 40 | * PERFORMANCE OF THIS SOFTWARE. 41 | */ 42 | 43 | #include 44 | #include 45 | #ifdef __APPLE__ 46 | #include 47 | #else 48 | #include 49 | #endif 50 | #include 51 | 52 | #include "kerberos.h" 53 | #include "globals.h" 54 | 55 | /* 56 | * Function: display_ctx_flags 57 | * 58 | * Purpose: displays the flags returned by context initiation in 59 | * a human-readable form 60 | * 61 | * Arguments: 62 | * 63 | * int ret_flags 64 | * 65 | * Effects: 66 | * 67 | * Strings corresponding to the context flags are printed on 68 | * stdout, preceded by "context flag: " and followed by a newline 69 | */ 70 | 71 | void display_ctx_flags(OM_uint32 flags) { 72 | if (flags & GSS_C_DELEG_FLAG) 73 | printf("context flag: GSS_C_DELEG_FLAG\n"); 74 | if (flags & GSS_C_MUTUAL_FLAG) 75 | printf("context flag: GSS_C_MUTUAL_FLAG\n"); 76 | if (flags & GSS_C_REPLAY_FLAG) 77 | printf("context flag: GSS_C_REPLAY_FLAG\n"); 78 | if (flags & GSS_C_SEQUENCE_FLAG) 79 | printf("context flag: GSS_C_SEQUENCE_FLAG\n"); 80 | if (flags & GSS_C_CONF_FLAG) 81 | printf("context flag: GSS_C_CONF_FLAG\n"); 82 | if (flags & GSS_C_INTEG_FLAG) 83 | printf("context flag: GSS_C_INTEG_FLAG\n"); 84 | } 85 | 86 | static void display_status_1(char *m, OM_uint32 code, int type) { 87 | OM_uint32 maj_stat; 88 | OM_uint32 min_stat; 89 | gss_buffer_desc msg; 90 | OM_uint32 msg_ctx; 91 | 92 | msg_ctx = 0; 93 | while (1) { 94 | maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID, 95 | &msg_ctx, &msg); 96 | if (maj_stat == GSS_S_COMPLETE) 97 | printf("GSS-API error %s: %s\n", m, (char *) msg.value); 98 | else if (maj_stat == GSS_S_BAD_MECH) 99 | printf("GSS-API error that could not be translated due to a bad mechanism (GSS_S_BAD_MECH)\n"); 100 | else if (maj_stat == GSS_S_BAD_STATUS) 101 | printf("GSS-API error that is unknown (or this function was called with a wrong status type) (GSS_S_BAD_STATUS)\n"); 102 | else if (maj_stat == GSS_S_FAILURE) 103 | printf("GSS-API error and gss_display_status failed with minor status code %lo (GSS_S_FAILURE)\n", (long unsigned int)min_stat); 104 | else 105 | printf("GSS-API error unrecognized return value from gss_display_status\n"); 106 | (void) gss_release_buffer(&min_stat, &msg); 107 | 108 | if (!msg_ctx) 109 | break; 110 | } 111 | } 112 | 113 | /* 114 | * Function: display_status 115 | * 116 | * Purpose: displays GSS-API messages 117 | * 118 | * Arguments: 119 | * 120 | * msg a string to be displayed with the message 121 | * maj_stat the GSS-API major status code 122 | * min_stat the GSS-API minor status code 123 | * 124 | * Effects: 125 | * 126 | * The GSS-API messages associated with maj_stat and min_stat are 127 | * displayed on stderr, each preceded by "GSS-API error : " and 128 | * followed by a newline. 129 | */ 130 | void display_status(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) { 131 | display_status_1(msg, maj_stat, GSS_C_GSS_CODE); 132 | if (maj_stat != GSS_S_COMPLETE) 133 | display_status_1(msg, min_stat, GSS_C_MECH_CODE); 134 | } 135 | 136 | void display_name(char* txt, gss_name_t *name) { 137 | OM_uint32 maj_stat; 138 | OM_uint32 min_stat; 139 | gss_buffer_desc out_name; 140 | 141 | maj_stat = gss_display_name(&min_stat, *name, &out_name, NULL); 142 | if (maj_stat != GSS_S_COMPLETE && debug) { 143 | display_status("Display name", maj_stat, min_stat); 144 | } 145 | 146 | printf("%s %s\n", txt, (char *)out_name.value); 147 | 148 | (void) gss_release_buffer(&min_stat, &out_name); 149 | } 150 | 151 | int acquire_name(gss_name_t *target_name, char *service_name, gss_OID oid) { 152 | gss_buffer_desc tmp_tok; 153 | OM_uint32 maj_stat; 154 | OM_uint32 min_stat; 155 | 156 | tmp_tok.value = service_name; 157 | tmp_tok.length = strlen(service_name) + 1; 158 | 159 | maj_stat = gss_import_name(&min_stat, &tmp_tok, oid, target_name); 160 | 161 | if (debug) { 162 | if (maj_stat != GSS_S_COMPLETE) { 163 | display_status("Parsing name", maj_stat, min_stat); 164 | } else { 165 | display_name("Acquired kerberos name", target_name); 166 | } 167 | } 168 | return maj_stat; 169 | } 170 | 171 | /* 172 | * Function: client_establish_context 173 | * 174 | * Purpose: establishes a GSS-API context with a specified service and 175 | * returns the context handle 176 | * 177 | * Arguments: 178 | * 179 | * service_name (r) the ASCII service name of the service 180 | * context (w) the established GSS-API context 181 | * ret_flags (w) the returned flags from init_sec_context 182 | * 183 | * Returns: 0 on success, -1 on failure 184 | * 185 | * Effects: 186 | * 187 | * service_name is imported as a GSS-API name and a GSS-API context is 188 | * established with the corresponding service; the service should be 189 | * listening on the TCP connection s. The default GSS-API mechanism 190 | * is used, and mutual authentication and replay detection are 191 | * requested. 192 | * 193 | * If successful, the context handle is returned in context. If 194 | * unsuccessful, the GSS-API error messages are displayed on stderr 195 | * and -1 is returned. 196 | */ 197 | int client_establish_context(char *service_name, 198 | OM_uint32 *ret_flags, gss_buffer_desc* send_tok) { 199 | gss_name_t target_name; 200 | gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; 201 | OM_uint32 maj_stat; 202 | OM_uint32 min_stat; 203 | OM_uint32 init_min_stat; 204 | 205 | if ((maj_stat = acquire_name(&target_name, service_name, 206 | GSS_C_NT_HOSTBASED_SERVICE)) != GSS_S_COMPLETE) 207 | return maj_stat; 208 | 209 | if (debug) { 210 | display_name("SPN name", &target_name); 211 | } 212 | 213 | maj_stat = gss_init_sec_context(&init_min_stat, GSS_C_NO_CREDENTIAL, 214 | &gss_context, 215 | target_name, 216 | GSS_C_NULL_OID,// use default mech 217 | 0, 0, // no special flags requested, no time req 218 | GSS_C_NO_CHANNEL_BINDINGS, /* no channel bindings */ 219 | GSS_C_NO_BUFFER, // no input buffer 220 | NULL, /* ignore mech type */ 221 | send_tok, ret_flags, //the returned token, the token flags 222 | NULL /* ignore time_rec */ 223 | ); 224 | 225 | gss_release_name(&min_stat, &target_name); 226 | 227 | if (maj_stat != GSS_S_COMPLETE) { 228 | if(maj_stat == GSS_S_CONTINUE_NEEDED){ 229 | //TODO 230 | } 231 | if (debug) { 232 | display_status("Initializing context", maj_stat, init_min_stat); 233 | } 234 | if (gss_context == GSS_C_NO_CONTEXT) 235 | gss_delete_sec_context(&min_stat, &gss_context, GSS_C_NO_BUFFER); 236 | return maj_stat; 237 | } 238 | 239 | if (debug) { 240 | printf("Got token (size=%d)\n", (int) send_tok->length); 241 | } 242 | maj_stat = gss_delete_sec_context(&min_stat, &gss_context, GSS_C_NO_BUFFER); 243 | if (maj_stat != GSS_S_COMPLETE && debug) { 244 | display_status("Deleting context", maj_stat, min_stat); 245 | } 246 | return GSS_S_COMPLETE; 247 | } 248 | 249 | 250 | 251 | /** 252 | * acquires a kerberos token for default credential using SPN HTTP@ 253 | */ 254 | int acquire_kerberos_token(const char* hostname, struct auth_s *credentials, 255 | char** buf, size_t *bufsize) { 256 | char service_name[BUFSIZE]; 257 | OM_uint32 ret_flags; 258 | OM_uint32 min_stat; 259 | 260 | if (credentials->haskrb == KRB_KO) { 261 | if (debug) 262 | printf("Skipping already failed gss auth for %s\n", hostname); 263 | return 0; 264 | } 265 | 266 | if (!(credentials->haskrb & KRB_CREDENTIAL_AVAILABLE)) { 267 | credentials->haskrb |= check_credential(); 268 | if (!(credentials->haskrb & KRB_CREDENTIAL_AVAILABLE)){ 269 | //no credential -> no token 270 | if (debug) 271 | printf("No valid credential available\n"); 272 | return 0; 273 | } 274 | } 275 | 276 | gss_buffer_desc send_tok; 277 | 278 | strlcpy(service_name, "HTTP@", BUFSIZE); 279 | strlcat(service_name, hostname, BUFSIZE); 280 | 281 | int rc = client_establish_context(service_name, &ret_flags, &send_tok); 282 | 283 | if (rc == GSS_S_COMPLETE) { 284 | char *token = NULL; 285 | size_t token_size; 286 | credentials->haskrb = KRB_OK; 287 | 288 | // approximately compute size of token in base64 289 | token_size = 4*send_tok.length; 290 | token_size /= 3; 291 | token_size += 4 + 4; 292 | if (token_size + 10 + 1 > *bufsize) { 293 | // *bufsize must be >= token_size + length of "NEGOTIATE " (10) + null terminator (1) 294 | *bufsize = token_size + 10 + 1; 295 | *buf = realloc(*buf, *bufsize); 296 | } 297 | 298 | strlcpy(*buf, "NEGOTIATE ", *bufsize); 299 | token = *buf + 10; 300 | 301 | to_base64((unsigned char *)token, send_tok.value, send_tok.length, token_size); 302 | 303 | if (debug) { 304 | printf("Token B64 (%d size=%d)... %s\n", (int)token_size, (int)strlen(token), token); 305 | display_ctx_flags(ret_flags); 306 | } 307 | 308 | rc=1; 309 | } else { 310 | credentials->haskrb = KRB_KO; 311 | 312 | if (debug) 313 | printf("No valid token acquired for %s\n", service_name); 314 | 315 | rc=0; 316 | } 317 | 318 | (void) gss_release_buffer(&min_stat, &send_tok); 319 | 320 | return rc; 321 | } 322 | 323 | /** 324 | * checks if a default cached credential is cached 325 | */ 326 | int check_credential(void) { 327 | OM_uint32 min_stat; 328 | gss_name_t name; 329 | OM_uint32 lifetime; 330 | gss_cred_usage_t cred_usage; 331 | gss_OID_set mechanisms; 332 | OM_uint32 maj_stat; 333 | 334 | maj_stat = gss_inquire_cred(&min_stat, GSS_C_NO_CREDENTIAL, &name, 335 | &lifetime, &cred_usage, &mechanisms); 336 | if (maj_stat != GSS_S_COMPLETE) { 337 | if (debug) { 338 | display_status("Inquire credential", maj_stat, min_stat); 339 | } 340 | return 0; 341 | } 342 | (void) gss_release_oid_set(&min_stat, &mechanisms); 343 | 344 | if (name != NULL) { 345 | if (debug) { 346 | display_name("Available cached credential", &name); 347 | } 348 | (void) gss_release_name(&min_stat, &name); 349 | return KRB_CREDENTIAL_AVAILABLE; 350 | } 351 | return 0; 352 | } 353 | -------------------------------------------------------------------------------- /ntlm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * These are NTLM authentication routines for the main module of CNTLM 3 | * 4 | * CNTLM is free software; you can redistribute it and/or modify it under the 5 | * terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | * St, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * Copyright (c) 2007 David Kubicek 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "ntlm.h" 27 | #include "swap.h" 28 | #include "xcrypt.h" 29 | #include "utils.h" 30 | #ifdef __CYGWIN__ 31 | #include "sspi.h" 32 | #endif 33 | 34 | extern int debug; 35 | 36 | static void ntlm_set_key(const unsigned char *src, gl_des_ctx *context) { 37 | char key[8]; 38 | 39 | key[0] = src[0]; 40 | key[1] = ((src[0] << 7) & 0xff) | (src[1] >> 1); 41 | key[2] = ((src[1] << 6) & 0xff) | (src[2] >> 2); 42 | key[3] = ((src[2] << 5) & 0xff) | (src[3] >> 3); 43 | key[4] = ((src[3] << 4) & 0xff) | (src[4] >> 4); 44 | key[5] = ((src[4] << 3) & 0xff) | (src[5] >> 5); 45 | key[6] = ((src[5] << 2) & 0xff) | (src[6] >> 6); 46 | key[7] = (src[6] << 1) & 0xff; 47 | 48 | gl_des_setkey(context, key); 49 | } 50 | 51 | static int ntlm_calc_resp(char **dst, char *keys, const char *challenge) { 52 | gl_des_ctx context; 53 | 54 | *dst = zmalloc(24 + 1); 55 | 56 | ntlm_set_key(MEM(keys, unsigned char, 0), &context); 57 | gl_des_ecb_encrypt(&context, challenge, *dst); 58 | 59 | ntlm_set_key(MEM(keys, unsigned char, 7), &context); 60 | gl_des_ecb_encrypt(&context, challenge, *dst+8); 61 | 62 | ntlm_set_key(MEM(keys, unsigned char, 14), &context); 63 | gl_des_ecb_encrypt(&context, challenge, *dst+16); 64 | 65 | return 24; 66 | } 67 | 68 | static void ntlm2_calc_resp(char **nthash, int *ntlen, char **lmhash, int *lmlen, 69 | const char *passnt2, char *challenge, int tbofs, int tblen) { 70 | char *tmp; 71 | char *blob; 72 | char *nonce; 73 | char *buf; 74 | int64_t tw; 75 | int blen; 76 | 77 | nonce = zmalloc(8 + 1); 78 | VAL(nonce, uint64_t, 0) = getrandom64(); 79 | tw = ((uint64_t)time(NULL) + 11644473600LLU) * 10000000LLU; 80 | 81 | if (debug) { 82 | tmp = printmem(nonce, 8, 7); 83 | #ifdef PRId64 84 | printf("NTLMv2:\n\t Nonce: %s\n\tTimestamp: %"PRId64"\n", tmp, tw); 85 | #else 86 | printf("NTLMv2:\n\t Nonce: %s\n\tTimestamp: %ld\n", tmp, tw); 87 | #endif 88 | free(tmp); 89 | } 90 | 91 | blob = zmalloc(4+4+8+8+4+tblen+4 + 1); 92 | VAL(blob, uint32_t, 0) = U32LE(0x00000101); 93 | VAL(blob, uint32_t, 4) = U32LE(0); 94 | VAL(blob, uint64_t, 8) = U64LE(tw); 95 | VAL(blob, uint64_t, 16) = U64LE(VAL(nonce, uint64_t, 0)); 96 | VAL(blob, uint32_t, 24) = U32LE(0); 97 | memcpy(blob+28, MEM(challenge, char, tbofs), tblen); 98 | memset(blob+28+tblen, 0, 4); 99 | blen = 28+tblen+4; 100 | 101 | if (0 && debug) { 102 | tmp = printmem(blob, blen, 7); 103 | printf("\t Blob: %s (%d)\n", tmp, blen); 104 | free(tmp); 105 | } 106 | 107 | *ntlen = 16+blen; 108 | *nthash = zmalloc(*ntlen + 1); 109 | buf = zmalloc(8+blen + 1); 110 | memcpy(buf, MEM(challenge, char, 24), 8); 111 | memcpy(buf+8, blob, blen); 112 | hmac_md5(passnt2, 16, buf, 8+blen, *nthash); 113 | memcpy(*nthash+16, blob, blen); 114 | free(buf); 115 | 116 | *lmlen = 24; 117 | *lmhash = zmalloc(*lmlen + 1); 118 | buf = zmalloc(16 + 1); 119 | memcpy(buf, MEM(challenge, char, 24), 8); 120 | memcpy(buf+8, nonce, 8); 121 | hmac_md5(passnt2, 16, buf, 16, *lmhash); 122 | memcpy(*lmhash+16, nonce, 8); 123 | free(buf); 124 | 125 | free(blob); 126 | free(nonce); 127 | return; 128 | } 129 | 130 | static void ntlm2sr_calc_rest(char **nthash, int *ntlen, char **lmhash, int *lmlen, char *passnt, char *challenge) { 131 | char *sess; 132 | char *nonce; 133 | char *buf; 134 | 135 | nonce = zmalloc(8 + 1); 136 | VAL(nonce, uint64_t, 0) = getrandom64(); 137 | 138 | *lmlen = 24; 139 | *lmhash = zmalloc(*lmlen + 1); 140 | memcpy(*lmhash, nonce, 8); 141 | memset(*lmhash+8, 0, 16); 142 | 143 | buf = zmalloc(16 + 1); 144 | sess = zmalloc(16 + 1); 145 | memcpy(buf, MEM(challenge, char, 24), 8); 146 | memcpy(buf+8, nonce, 8); 147 | md5_buffer(buf, 16, sess); 148 | free(buf); 149 | 150 | *ntlen = 24; 151 | ntlm_calc_resp(nthash, passnt, sess); 152 | 153 | free(sess); 154 | free(nonce); 155 | return; 156 | } 157 | 158 | char *ntlm_hash_lm_password(const char *password) { 159 | char magic[8] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; 160 | gl_des_ctx context; 161 | char *keys; 162 | char *pass; 163 | 164 | keys = zmalloc(21 + 1); 165 | pass = zmalloc(14 + 1); 166 | uppercase(strncpy(pass, password, MIN(14, strlen(password)))); 167 | 168 | ntlm_set_key(MEM(pass, unsigned char, 0), &context); 169 | gl_des_ecb_encrypt(&context, magic, keys); 170 | 171 | ntlm_set_key(MEM(pass, unsigned char, 7), &context); 172 | gl_des_ecb_encrypt(&context, magic, keys+8); 173 | 174 | memset(keys+16, 0, 5); 175 | compat_memset_s(pass, 15, 0, 14); 176 | free(pass); 177 | 178 | return keys; 179 | } 180 | 181 | char *ntlm_hash_nt_password(const char *password) { 182 | char *u16; 183 | char *keys; 184 | size_t len; 185 | 186 | keys = zmalloc(21 + 1); 187 | len = unicode(&u16, password); 188 | md4_buffer(u16, len, keys); 189 | 190 | memset(keys+16, 0, 5); 191 | compat_memset_s(u16, len, 0, len); 192 | free(u16); 193 | 194 | return keys; 195 | } 196 | 197 | char *ntlm2_hash_password(const char *username, const char *domain, const char *password) { 198 | char *tmp; 199 | char *buf; 200 | char *passnt; 201 | char *passnt2; 202 | size_t len; 203 | 204 | passnt = ntlm_hash_nt_password(password); 205 | 206 | const size_t buf_len = strlen(username) + strlen(domain) + 1; 207 | buf = zmalloc(buf_len); 208 | strlcat(buf, username, buf_len); 209 | strlcat(buf, domain, buf_len); 210 | uppercase(buf); 211 | len = unicode(&tmp, buf); 212 | 213 | passnt2 = zmalloc(16 + 1); 214 | hmac_md5(passnt, 16, tmp, len, passnt2); 215 | 216 | free(passnt); 217 | free(tmp); 218 | free(buf); 219 | 220 | return passnt2; 221 | } 222 | 223 | int ntlm_request(char **dst, struct auth_s *creds) { 224 | #ifdef __CYGWIN__ 225 | if (sspi_enabled()) 226 | { 227 | return sspi_request(dst, &creds->sspi); 228 | } 229 | #endif 230 | char *buf; 231 | char *tmp; 232 | int dlen; 233 | int hlen; 234 | uint32_t flags = 0xb206; 235 | 236 | *dst = NULL; 237 | dlen = strlen(creds->domain); 238 | hlen = strlen(creds->workstation); 239 | 240 | if (!creds->flags) { 241 | if (creds->hashntlm2) 242 | flags = 0xa208b205; 243 | else if (creds->hashnt == 2) 244 | flags = 0xa208b207; 245 | else if (creds->hashnt && creds->hashlm) 246 | flags = 0xb207; 247 | else if (creds->hashnt) 248 | flags = 0xb205; 249 | else if (creds->hashlm) 250 | flags = 0xb206; 251 | else { 252 | if (debug) { 253 | printf("You're requesting with empty auth_s?!\n"); 254 | dump_auth(creds); 255 | } 256 | return 0; 257 | } 258 | } else 259 | flags = creds->flags; 260 | 261 | if (debug) { 262 | printf("NTLM Request:\n"); 263 | printf("\t Domain: %s\n", creds->domain); 264 | printf("\t Hostname: %s\n", creds->workstation); 265 | printf("\t Flags: 0x%X\n", (int)flags); 266 | } 267 | 268 | buf = zmalloc(NTLM_BUFSIZE); 269 | memcpy(buf, "NTLMSSP\0", 8); 270 | VAL(buf, uint32_t, 8) = U32LE(1); 271 | VAL(buf, uint32_t, 12) = U32LE(flags); 272 | VAL(buf, uint16_t, 16) = U16LE(dlen); 273 | VAL(buf, uint16_t, 18) = U16LE(dlen); 274 | VAL(buf, uint32_t, 20) = U32LE(32 + hlen); 275 | VAL(buf, uint16_t, 24) = U16LE(hlen); 276 | VAL(buf, uint16_t, 26) = U16LE(hlen); 277 | VAL(buf, uint32_t, 28) = U32LE(32); 278 | 279 | tmp = uppercase(strdup(creds->workstation)); 280 | memcpy(buf+32, tmp, hlen); 281 | free(tmp); 282 | 283 | tmp = uppercase(strdup(creds->domain)); 284 | memcpy(buf+32+hlen, tmp, dlen); 285 | free(tmp); 286 | 287 | *dst = buf; 288 | return 32+dlen+hlen; 289 | } 290 | 291 | static char *printuc(const char *src, size_t len) { 292 | char *tmp; 293 | size_t i; 294 | 295 | tmp = zmalloc((len+1)/2 + 1); 296 | for (i = 0; i < len/2; ++i) { 297 | tmp[i] = src[i*2]; 298 | } 299 | 300 | return tmp; 301 | } 302 | 303 | /* 304 | void dump(char *src, int len) { 305 | int i, j; 306 | char *tmp; 307 | 308 | tmp = new(len*3+4); 309 | for (i = 0; i < len; ++i) { 310 | snprintf(tmp+i*3, 4, "%0hhX ", src[i]); 311 | printf("%c ", src[i]); 312 | } 313 | printf("\n%s\n", tmp); 314 | free(tmp); 315 | } 316 | */ 317 | 318 | int ntlm_response(char **dst, char *challenge, int challen, struct auth_s *creds) { 319 | #ifdef __CYGWIN__ 320 | if (sspi_enabled()) 321 | { 322 | return sspi_response(dst, challenge, challen, &creds->sspi); 323 | } 324 | #endif 325 | char *buf; 326 | char *udomain; 327 | char *uuser; 328 | char *uhost; 329 | char *tmp; 330 | size_t dlen; 331 | size_t ulen; 332 | size_t hlen; 333 | uint16_t tpos; 334 | uint16_t tlen; 335 | uint16_t ttype = -1; 336 | uint16_t tbofs = 0; 337 | uint16_t tblen = 0; 338 | char *lmhash = NULL; 339 | char *nthash = NULL; 340 | int lmlen = 0; 341 | int ntlen = 0; 342 | 343 | if (debug) { 344 | printf("NTLM Challenge:\n"); 345 | tmp = printmem(MEM(challenge, char, 24), 8, 7); 346 | printf("\tChallenge: %s (len: %d)\n", tmp, challen); 347 | free(tmp); 348 | printf("\t Flags: 0x%X\n", U32LE(VAL(challenge, uint32_t, 20))); 349 | } 350 | 351 | if (challen >= NTLM_CHALLENGE_MIN) { 352 | tbofs = tpos = U16LE(VAL(challenge, uint16_t, 44)); 353 | while (tpos+4 <= challen && (ttype = U16LE(VAL(challenge, uint16_t, tpos)))) { 354 | tlen = U16LE(VAL(challenge, uint16_t, tpos+2)); 355 | if (tpos+4+tlen > challen) 356 | break; 357 | 358 | if (debug) { 359 | switch (ttype) { 360 | case 0x1: 361 | printf("\t Server: "); 362 | break; 363 | case 0x2: 364 | printf("\tNT domain: "); 365 | break; 366 | case 0x3: 367 | printf("\t FQDN: "); 368 | break; 369 | case 0x4: 370 | printf("\t Domain: "); 371 | break; 372 | case 0x5: 373 | printf("\t TLD: "); 374 | break; 375 | default: 376 | printf("\t %3d: ", ttype); 377 | break; 378 | } 379 | tmp = printuc(MEM(challenge, char, tpos+4), tlen); 380 | printf("%s\n", tmp); 381 | free(tmp); 382 | } 383 | 384 | tpos += 4+tlen; 385 | tblen += 4+tlen; 386 | } 387 | 388 | if (tblen && ttype == 0) 389 | tblen += 4; 390 | 391 | if (debug) { 392 | printf("\t TBofs: %d\n\t TBlen: %d\n\t ttype: %d\n", tbofs, tblen, ttype); 393 | } 394 | } 395 | 396 | if (creds->hashntlm2) { 397 | ntlm2_calc_resp(&nthash, &ntlen, &lmhash, &lmlen, creds->passntlm2, challenge, tbofs, tblen); 398 | } 399 | 400 | if (creds->hashnt == 2) { 401 | ntlm2sr_calc_rest(&nthash, &ntlen, &lmhash, &lmlen, creds->passnt, challenge); 402 | } 403 | 404 | if (creds->hashnt == 1) { 405 | ntlen = ntlm_calc_resp(&nthash, creds->passnt, MEM(challenge, char, 24)); 406 | } 407 | 408 | if (creds->hashlm) { 409 | lmlen = ntlm_calc_resp(&lmhash, creds->passlm, MEM(challenge, char, 24)); 410 | } 411 | 412 | if (creds->hashnt || creds->hashntlm2) { 413 | tmp = uppercase(strdup(creds->domain)); 414 | dlen = unicode(&udomain, tmp); 415 | free(tmp); 416 | ulen = unicode(&uuser, creds->user); 417 | tmp = uppercase(strdup(creds->workstation)); 418 | hlen = unicode(&uhost, tmp); 419 | free(tmp); 420 | } else { 421 | udomain = uppercase(strdup(creds->domain)); 422 | uuser = uppercase(strdup(creds->user)); 423 | uhost = uppercase(strdup(creds->workstation)); 424 | 425 | dlen = strlen(creds->domain); 426 | ulen = strlen(creds->user); 427 | hlen = strlen(creds->workstation); 428 | } 429 | 430 | if (debug) { 431 | printf("NTLM Response:\n"); 432 | printf("\t Hostname: '%s'\n", creds->workstation); 433 | printf("\t Domain: '%s'\n", creds->domain); 434 | printf("\t Username: '%s'\n", creds->user); 435 | if (ntlen) { 436 | tmp = printmem(nthash, ntlen, 7); 437 | printf("\t Response: '%s' (%d)\n", tmp, ntlen); 438 | free(tmp); 439 | } 440 | if (lmlen) { 441 | tmp = printmem(lmhash, lmlen, 7); 442 | printf("\t Response: '%s' (%d)\n", tmp, lmlen); 443 | free(tmp); 444 | } 445 | } 446 | 447 | buf = zmalloc(NTLM_BUFSIZE); 448 | memcpy(buf, "NTLMSSP\0", 8); 449 | VAL(buf, uint32_t, 8) = U32LE(3); 450 | 451 | /* LM */ 452 | VAL(buf, uint16_t, 12) = U16LE(lmlen); 453 | VAL(buf, uint16_t, 14) = U16LE(lmlen); 454 | VAL(buf, uint32_t, 16) = U32LE(64+dlen+ulen+hlen); 455 | 456 | /* NT */ 457 | VAL(buf, uint16_t, 20) = U16LE(ntlen); 458 | VAL(buf, uint16_t, 22) = U16LE(ntlen); 459 | VAL(buf, uint32_t, 24) = U32LE(64+dlen+ulen+hlen+lmlen); 460 | 461 | /* Domain */ 462 | VAL(buf, uint16_t, 28) = U16LE(dlen); 463 | VAL(buf, uint16_t, 30) = U16LE(dlen); 464 | VAL(buf, uint32_t, 32) = U32LE(64); 465 | 466 | /* Username */ 467 | VAL(buf, uint16_t, 36) = U16LE(ulen); 468 | VAL(buf, uint16_t, 38) = U16LE(ulen); 469 | VAL(buf, uint32_t, 40) = U32LE(64+dlen); 470 | 471 | /* Hostname */ 472 | VAL(buf, uint16_t, 44) = U16LE(hlen); 473 | VAL(buf, uint16_t, 46) = U16LE(hlen); 474 | VAL(buf, uint32_t, 48) = U32LE(64+dlen+ulen); 475 | 476 | /* Session */ 477 | VAL(buf, uint16_t, 52) = U16LE(0); 478 | VAL(buf, uint16_t, 54) = U16LE(0); 479 | VAL(buf, uint16_t, 56) = U16LE(64+dlen+ulen+hlen+lmlen+ntlen); 480 | 481 | /* Flags */ 482 | VAL(buf, uint32_t, 60) = VAL(challenge, uint32_t, 20); 483 | 484 | memcpy(MEM(buf, char, 64), udomain, dlen); 485 | memcpy(MEM(buf, char, 64+dlen), uuser, ulen); 486 | memcpy(MEM(buf, char, 64+dlen+ulen), uhost, hlen); 487 | if (lmhash) 488 | memcpy(MEM(buf, char, 64+dlen+ulen+hlen), lmhash, lmlen); 489 | if (nthash) 490 | memcpy(MEM(buf, char, 64+dlen+ulen+hlen+24), nthash, ntlen); 491 | 492 | if (nthash) 493 | free(nthash); 494 | if (lmhash) 495 | free(lmhash); 496 | 497 | free(uhost); 498 | free(uuser); 499 | free(udomain); 500 | 501 | *dst = buf; 502 | return 64+dlen+ulen+hlen+lmlen+ntlen; 503 | } 504 | --------------------------------------------------------------------------------