├── 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 ├── Software Updates.url ├── Support Website.url ├── README.txt └── setup.iss.in ├── config ├── strdup.c ├── socklen_t.c ├── gethostname.c └── endian.c ├── README.md ├── COPYRIGHT ├── direct.h ├── pages.h ├── rpm ├── cntlm.sysconfig ├── rules ├── cntlm.spec └── cntlm.init ├── scanner.h ├── acl.h ├── forward.h ├── ntlm.h ├── socket.h ├── kerberos.h ├── config.h ├── Makefile.xlc ├── globals.h ├── http.h ├── configure ├── auth.h ├── swap.h ├── pages.c ├── doc ├── valgrind.txt └── cntlm.conf ├── acl.c ├── auth.c ├── config.c ├── xcrypt.h ├── Makefile.orig ├── utils.h ├── Makefile ├── README ├── socket.c ├── scanner.c ├── kerberos.c ├── direct.c ├── ntlm.c ├── http.c ├── LICENSE └── utils.c /VERSION: -------------------------------------------------------------------------------- 1 | 0.92.3 2 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 5 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/metaphox/cntlm-gss/HEAD/win/cntlm.ico -------------------------------------------------------------------------------- /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/Support Website.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=http://sourceforge.net/tracker/?group_id=197861 3 | -------------------------------------------------------------------------------- /config/strdup.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) { 4 | return !strcmp("hello", strdup("hello")); 5 | } 6 | -------------------------------------------------------------------------------- /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.92.3) 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/gethostname.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) { 4 | char *tmp[300]; 5 | 6 | memset(tmp, 0, sizeof(tmp)); 7 | gethostname(tmp, sizeof(tmp)-1); 8 | if (strlen(tmp)) 9 | printf("%s", tmp); 10 | 11 | return !(!strlen(tmp)); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /config/endian.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is [Cntlm](http://cntlm.sourceforge.net/) **with Kerberos patch applied**. 2 | 3 | Works on a Ubuntu 12.04 box, at least for me. 4 | 5 | Dependency: [Kerberos](http://web.mit.edu/kerberos/). 6 | 7 | If Kerberos is compiled to a different location, say, $HOME/usr, compile Cntlm with 8 | 9 | `./configure --enable-kerberos` 10 | 11 | `export LIBRARY_PATH=$HOME/usr/lib` 12 | 13 | `export C_INCLUDE_PATH=$HOME/usr/include` 14 | 15 | `make` 16 | 17 | To run it, try `cntlm --help` or `cntlm -v` and fix whatever it complains. 18 | 19 | I have only the following lines in my ctnlm.conf file: 20 | 21 | ``` 22 | Username 23 | Domain 24 | Password 25 | Proxy proxy.server.domain.com:3128 26 | NoProxy localhost, 127.0.0.*, 10.*, 192.168.* 27 | Listen 3128 28 | ``` 29 | 30 | The username, domain and password are all unset. 31 | 32 | I could start it with `/home/me/usr/opt/cntlm-0.92.3/cntlm -a gss -c /home/me/usr/opt/cntlm-0.92.3/cntlm.conf` . 33 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | CNTLM - Authenticating HTTP Proxy 2 | Copyright (C) 2007-2010 David Kubicek 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | 18 | If you would like to negotiate alternate licensing terms, you may do 19 | so by contacting: David Kubicek , 20 | 21 | 22 | -------------------------------------------------------------------------------- /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_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 | #include "utils.h" 21 | #include "string.h" 22 | #include "stdio.h" 23 | 24 | extern char *gen_407_page(const char *http); 25 | extern char *gen_401_page(const char *http, const char *host, int port); 26 | extern char *gen_denied_page(const char *ip); 27 | extern char *gen_502_page(const char *http, const char *msg); 28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 25 | /* 26 | * ISA plugin flags 27 | */ 28 | #define PLUG_NONE 0x0000 29 | #define PLUG_SENDHEAD 0x0001 30 | #define PLUG_SENDDATA 0x0002 31 | #define PLUG_ERROR 0x8000 32 | #define PLUG_ALL 0x7FFF 33 | 34 | /* 35 | * Plugin download sample size 36 | */ 37 | #define SAMPLE 4096 38 | 39 | extern int scanner_hook(rr_data_t request, rr_data_t response, struct auth_s *credentials, int cd, int *sd, long maxKBs); 40 | 41 | #endif /* _SCANNER_H */ 42 | -------------------------------------------------------------------------------- /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_t rules, struct in_addr naddr); 44 | 45 | #endif /* _ACL_H */ 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 proxy_connect(struct auth_s *credentials); 27 | extern int proxy_authenticate(int *sd, rr_data_t request, rr_data_t response, struct auth_s *creds); 28 | extern int prepare_http_connect(int sd, struct auth_s *credentials, const char *thost); 29 | extern rr_data_t forward_request(void *cdata, rr_data_t request); 30 | extern void forward_tunnel(void *thread_data); 31 | extern void magic_auth_detect(const char *url); 32 | 33 | #endif /* _FORWARD_H */ 34 | -------------------------------------------------------------------------------- /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 "xcrypt.h" 26 | #include "auth.h" 27 | 28 | #define NTLM_BUFSIZE 1024 29 | #define NTLM_CHALLENGE_MIN 24 30 | 31 | extern char *ntlm_hash_lm_password(char *password); 32 | extern char *ntlm_hash_nt_password(char *password); 33 | extern char *ntlm2_hash_password(char *username, char *domain, char *password); 34 | extern int ntlm_request(char **dst, struct auth_s *creds); 35 | extern int ntlm_response(char **dst, char *challenge, int challen, struct auth_s *creds); 36 | 37 | #endif /* _NTLM_H */ 38 | -------------------------------------------------------------------------------- /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 | 28 | #include "config/config.h" 29 | 30 | #if config_socklen_t != 1 31 | #define socklen_t uint32_t 32 | #endif 33 | 34 | #ifndef INADDR_LOOPBACK 35 | #define INADDR_LOOPBACK 0x7f000001 36 | #endif 37 | 38 | extern int so_resolv(struct in_addr *host, const char *name); 39 | extern int so_connect(struct in_addr host, int port); 40 | extern int so_listen(int port, struct in_addr source); 41 | extern int so_dataready(int fd); 42 | extern int so_closed(int fd); 43 | extern int so_recvln(int fd, char **buf, int *size); 44 | 45 | #endif /* _SOCKET_H */ 46 | -------------------------------------------------------------------------------- /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 "globals.h" 31 | #include "auth.h" 32 | 33 | //used in global auth flag 34 | #define KRB_NO_CREDS 0 35 | #define KRB_CREDENTIAL_AVAILABLE 1 36 | #define KRB_FORCE_USE_KRB 2 37 | 38 | //used while auth 39 | #define KRB_NOT_TRIED 0 40 | #define KRB_OK 1 41 | #define KRB_KO 4 42 | 43 | /** 44 | * acquires a kerberos token for default credential using SPN HTTP@ 45 | */ 46 | int acquire_kerberos_token(proxy_t* proxy, struct auth_s *credentials, char* buf); 47 | 48 | /** 49 | * checks if a default cached credential is cached 50 | */ 51 | int check_credential(); 52 | 53 | int acquire_credential(struct auth_s *credentials); 54 | 55 | #endif /* KERBEROS_H_ */ 56 | -------------------------------------------------------------------------------- /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_OPTION(cf, opt, var, size) { char *__tmp = NULL; if ((__tmp=config_pop(cf, opt))) { strlcpy(var, __tmp, size); } if (__tmp) free(__tmp); } 30 | #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); } 31 | 32 | typedef struct config_s *config_t; 33 | struct config_s { 34 | hlist_t options; 35 | }; 36 | 37 | extern config_t config_open(const char *fname); 38 | extern void config_set(config_t cf, char *option, char *value); 39 | extern char *config_pop(config_t cf, const char *option); 40 | extern int config_count(config_t cf); 41 | extern void config_close(config_t cf); 42 | 43 | #endif /* _CONFIG_H */ 44 | -------------------------------------------------------------------------------- /Makefile.xlc: -------------------------------------------------------------------------------- 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 | SYSCONFDIR=/usr/local/etc 6 | BINDIR=/usr/local/bin 7 | MANDIR=/usr/local/man 8 | 9 | # 10 | # 11 | CC=xlc_r 12 | OBJS=utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o main.o 13 | CFLAGS=$(FLAGS) -O3 -D_POSIX_C_SOURCE=200112 -D_ISOC99_SOURCE -D_REENTRANT -DVERSION=\"`cat VERSION`\" 14 | LDFLAGS=-lpthread 15 | NAME=cntlm 16 | 17 | $(NAME): $(OBJS) 18 | $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) 19 | 20 | main.o: main.c 21 | if [ -z "$(SYSCONFDIR)" ]; then \ 22 | $(CC) $(CFLAGS) -c main.c -o $@; \ 23 | else \ 24 | $(CC) $(CFLAGS) -DSYSCONFDIR=\"$(SYSCONFDIR)\" -c main.c -o $@; \ 25 | fi 26 | 27 | install: $(NAME) 28 | if [ -f /usr/bin/oslevel ]; then \ 29 | install -M 0755 -S -f $(BINDIR) $(NAME); \ 30 | install -M 0644 -f $(MANDIR)/man1 doc/$(NAME).1; \ 31 | install -M 0600 -c $(SYSCONFDIR) doc/$(NAME).conf; \ 32 | else \ 33 | install -D -m 0755 -s $(NAME) $(BINDIR)/$(NAME); \ 34 | install -D -m 0644 doc/$(NAME).1 $(MANDIR)/man1/$(NAME).1; \ 35 | [ -f $(SYSCONFDIR)/$(NAME).conf -o -z "$(SYSCONFDIR)" ] \ 36 | || install -D -m 0600 doc/$(NAME).conf $(SYSCONFDIR)/$(NAME).conf; \ 37 | fi 38 | 39 | uninstall: 40 | rm -f $(BINDIR)/$(NAME) $(MANDIR)/man1/$(NAME).1 2>/dev/null || true 41 | 42 | clean: 43 | @rm -f *.o cntlm cntlm.exe configure-stamp build-stamp config/config.h 2>/dev/null 44 | @rm -f cntlm-install win/cyg* win/cntlm* 2>/dev/null 45 | @rm -f config/endian config/gethostname config/strdup config/socklen_t config/*.exe 46 | @if [ -h Makefile ]; then rm -f Makefile; mv Makefile.gcc Makefile; fi 47 | 48 | distclean: clean 49 | @rm -f *.deb *.rpm *.tgz *.tar.gz *.tar.bz2 tags ctags pid 2>/dev/null 50 | -------------------------------------------------------------------------------- /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) 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_clean -k 49 | dh_installdirs 50 | 51 | # Add here commands to install the package into debian/cntlm. 52 | $(MAKE) DESTDIR=$(CURDIR)/debian/cntlm 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 | -------------------------------------------------------------------------------- /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 parent_count; 49 | extern plist_t parent_list; 50 | 51 | /* 52 | * just malloc/free sizeof(proxy_t) 53 | */ 54 | typedef struct { 55 | char hostname[64]; 56 | struct auth_s creds; 57 | struct in_addr host; 58 | int port; 59 | int resolved; 60 | } proxy_t; 61 | 62 | extern hlist_t header_list; /* forward_request() */ 63 | extern hlist_t users_list; /* socks5_thread() */ 64 | extern plist_t scanner_agent_list; /* scanner_hook() */ 65 | extern plist_t noproxy_list; /* proxy_thread() */ 66 | 67 | #endif /* _GLOBALS_H */ 68 | -------------------------------------------------------------------------------- /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_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_t data); 45 | extern int tunnel(int cd, int sd); 46 | extern length_t http_has_body(rr_data_t request, rr_data_t response); 47 | extern int http_body_send(int writefd, int readfd, rr_data_t request, rr_data_t response); 48 | extern int http_body_drop(int fd, rr_data_t response); 49 | 50 | #endif /* _HTTP_H */ 51 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Search for GCC or XL C/C++, former if both exist 4 | # 5 | # To add another compiler, just create Makefile.XXX and add XXX to $CCS 6 | # 7 | # To prevent ugly Makefile extensions, underscore and chars following it 8 | # in the name XXX are automatically removed before locating relevant 9 | # Makefile. This is why compiler "xlc_r" has Makefile extension "xlc". 10 | # This can be disabled if neccessary. 11 | # 12 | 13 | CCS="xlc_r gcc" 14 | 15 | # 16 | # Look for supported compilers 17 | # 18 | for c in $CCS; do 19 | if CCPATH=`which $c 2>&1` && [ -z "${CCPATH%%/*}" ]; then 20 | CC="$c" 21 | break 22 | fi 23 | done 24 | 25 | # 26 | # Make a link to a proper Makefile.* 27 | # 28 | if [ -z "$CC" ]; then 29 | echo "Unable to find GNU GCC or IBM XL C/C++. Fix your PATH!" 30 | exit 1 31 | else 32 | echo "Using $CCPATH to compile Cntlm" 33 | [ -h Makefile ] && rm -f Makefile 2>/dev/null 34 | case "$CC" in 35 | gcc) 36 | # default Makefile is for GCC; just revert back to 37 | # GCC if Makefile is linked to other compiler version 38 | if [ ! -f Makefile ]; then 39 | mv Makefile.gcc Makefile 40 | fi 41 | ;; 42 | *) 43 | # backup default GCC Makefile and create a link to other 44 | if [ -f Makefile ]; then 45 | mv Makefile Makefile.gcc 46 | fi 47 | 48 | EXT=`echo "$CC" | sed 's/_.*//'` 49 | ln -s Makefile.$EXT Makefile 50 | ;; 51 | esac 52 | fi 53 | 54 | STAMP=configure-stamp 55 | CONFIG=config/config.h 56 | TESTS="endian strdup socklen_t gethostname" 57 | 58 | #[ -f $STAMP ] && exit 0 59 | touch $STAMP 60 | 61 | rm -f $CONFIG 62 | for i in $TESTS; do 63 | printf "Checking $i... " 64 | printf "#define config_$i " >> $CONFIG 65 | OUT=`$CC -D_POSIX_C_SOURCE=199506L -D_ISOC99_SOURCE -D_REENTRANT -o config/$i config/$i.c 2>&1` 66 | rc=$? 67 | 68 | if [ $rc -ne 0 ]; then # -o -n "$OUT" ]; then 69 | rc=0 70 | RET=no 71 | else 72 | RET=`./config/$i` 73 | rc=$? 74 | [ -z "$RET" ] && if [ $rc -eq 0 ]; then RET="no"; else RET=yes; fi 75 | fi 76 | 77 | echo $rc >> $CONFIG 78 | echo $RET 79 | done 80 | 81 | while [ $1 ] 82 | do 83 | case $1 in 84 | --enable-kerberos) 85 | printf "#define ENABLE_KERBEROS" >> $CONFIG 86 | ;; 87 | *) 88 | echo "Unknown flag $1" 89 | rm -f $CONFIG 90 | ;; 91 | esac 92 | shift 93 | done -------------------------------------------------------------------------------- /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 | 29 | /* 30 | * Although I always prefer structs with pointer refs, I need direct storage 31 | * here to be able to alloc/free it in one go. It is used in a plist_t which 32 | * frees its items, but not recursively. 33 | */ 34 | struct auth_s { 35 | char user[MINIBUF_SIZE]; 36 | char domain[MINIBUF_SIZE]; 37 | char workstation[MINIBUF_SIZE]; 38 | char passlm[MINIBUF_SIZE]; 39 | char passnt[MINIBUF_SIZE]; 40 | char passntlm2[MINIBUF_SIZE]; 41 | int hashntlm2; 42 | int hashnt; 43 | int hashlm; 44 | #ifdef ENABLE_KERBEROS 45 | int haskrb; 46 | #endif 47 | uint32_t flags; 48 | }; 49 | 50 | #define auth_strcpy(creds, var, value) \ 51 | if ((creds) && (value)) { \ 52 | strlcpy(((creds)->var), (value), MINIBUF_SIZE); \ 53 | } 54 | 55 | #define auth_memcpy(creds, var, value, len) \ 56 | if ((creds) && (value)) { \ 57 | memcpy(((creds)->var), (value), MIN(len, MINIBUF_SIZE)); \ 58 | } 59 | 60 | /* 61 | * No free_auth() required, just use free() 62 | * new_auth() is also just a convenience malloc/memset() wrapper 63 | */ 64 | extern struct auth_s *new_auth(void); 65 | extern struct auth_s *copy_auth(struct auth_s *dst, struct auth_s *src, int fullcopy); 66 | extern struct auth_s *dup_auth(struct auth_s *creds, int fullcopy); 67 | extern void dump_auth(struct auth_s *creds); 68 | 69 | #endif /* _AUTH_H */ 70 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-2010 David Kubicek 6 | AppPublisher=David Kubicek 7 | AppPublisherURL=http://cntlm.sf.net/ 8 | LicenseFile=license.txt 9 | 10 | DefaultDirName={pf}\Cntlm 11 | DefaultGroupName=Cntlm 12 | SetupIconFile=cntlm.ico 13 | UninstallDisplayIcon={app}\cntlm.ico 14 | Uninstallable=yes 15 | OutputBaseFileName=cntlm-$VERSION-setup 16 | OutputDir=. 17 | 18 | [Files] 19 | Source: "cntlm.exe"; DestDir: "{app}" 20 | Source: "cygrunsrv.exe"; DestDir: "{app}" 21 | Source: "cygwin1.dll"; DestDir: "{app}" 22 | Source: "cyggcc_s-1.dll"; DestDir: "{app}" 23 | Source: "cygstdc++-6.dll"; DestDir: "{app}" 24 | Source: "cntlm.ini"; DestDir: "{app}"; Flags: uninsneveruninstall confirmoverwrite 25 | Source: "cntlm_manual.pdf"; DestDir: "{app}" 26 | Source: "README.txt"; DestDir: "{app}"; Flags: isreadme 27 | Source: "Cntlm Homepage.url"; DestDir: "{app}" 28 | Source: "Software Updates.url"; DestDir: "{app}" 29 | Source: "Support Website.url"; DestDir: "{app}" 30 | 31 | [Run] 32 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Stopping Cntlm service..."; Parameters: " --stop cntlm" 33 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Removing Cntlm service..."; Parameters: " --remove cntlm" 34 | 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" 35 | 36 | [Icons] 37 | Name: "{group}\cntlm.ini"; Filename: "{app}\cntlm.ini" 38 | Name: "{group}\Start Cntlm Authentication Proxy"; Filename: "{sys}\net.exe"; Parameters: "start cntlm"; WorkingDir: {app} 39 | Name: "{group}\Stop Cntlm Authentication Proxy"; Filename: "{sys}\net.exe"; Parameters: "stop cntlm"; WorkingDir: {app} 40 | Name: "{group}\Tools\Uninstall Cntlm"; Filename: "{uninstallexe}" 41 | Name: "{group}\Tools\Cntlm Homepage"; Filename: "{app}\Cntlm Homepage.url" 42 | Name: "{group}\Tools\Software Updates"; Filename: "{app}\Software Updates.url" 43 | Name: "{group}\Tools\Support Website"; Filename: "{app}\Support Website.url" 44 | Name: "{group}\Tools\PDF configuration manual"; Filename: "{app}\cntlm_manual.pdf" 45 | 46 | [UninstallRun] 47 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Stopping Cntlm service..."; Parameters: " --stop cntlm" 48 | Filename: "{app}\cygrunsrv.exe"; StatusMsg: "Removing Cntlm service..."; Parameters: " --remove cntlm" 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | #ifndef _PAGES_H 23 | #define _PAGES_H 24 | 25 | #include "utils.h" 26 | #include "string.h" 27 | #include "stdio.h" 28 | 29 | char *gen_407_page(const char *http) { 30 | char *tmp; 31 | if (http == NULL) 32 | http = "HTTP/1.0"; 33 | tmp = new(BUFSIZE); 34 | snprintf(tmp, BUFSIZE-1, 35 | "%s 407 Access denied\r\n" 36 | "Proxy-Authenticate: Basic realm=\"Cntlm Proxy\"\r\n" 37 | "Content-Type: text/html\r\n\r\n" 38 | "

407 Access denied

Cntlm requests your credentials for proxy access.

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

401 Access denied

Cntlm proxy requests your credentials for this URL.

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

Access denied

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

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

502 %s

Cntlm proxy failed to complete the request.

", 81 | http, msg, msg); 82 | return tmp; 83 | } 84 | 85 | #endif /* _PAGES_H */ 86 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /doc/cntlm.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Cntlm Authentication Proxy Configuration 3 | # 4 | # NOTE: all values are parsed literally, do NOT escape spaces, 5 | # do not quote. Use 0600 perms if you use plaintext password. 6 | # 7 | 8 | Username testuser 9 | Domain corp-uk 10 | Password password 11 | # NOTE: Use plaintext password only at your own risk 12 | # Use hashes instead. You can use a "cntlm -M" and "cntlm -H" 13 | # command sequence to get the right config for your environment. 14 | # See cntlm man page 15 | # Example secure config shown below. 16 | # PassLM 1AD35398BE6565DDB5C4EF70C0593492 17 | # PassNT 77B9081511704EE852F94227CF48A793 18 | ### Only for user 'testuser', domain 'corp-uk' 19 | # PassNTLMv2 D5826E9C665C37C80B53397D5C07BBCB 20 | 21 | # Specify the netbios hostname cntlm will send to the parent 22 | # proxies. Normally the value is auto-guessed. 23 | # 24 | # Workstation netbios_hostname 25 | 26 | # List of parent proxies to use. More proxies can be defined 27 | # one per line in format : 28 | # 29 | Proxy 10.0.0.41:8080 30 | Proxy 10.0.0.42:8080 31 | 32 | # List addresses you do not want to pass to parent proxies 33 | # * and ? wildcards can be used 34 | # 35 | NoProxy localhost, 127.0.0.*, 10.*, 192.168.* 36 | 37 | # Specify the port cntlm will listen on 38 | # You can bind cntlm to specific interface by specifying 39 | # the appropriate IP address also in format : 40 | # Cntlm listens on 127.0.0.1:3128 by default 41 | # 42 | Listen 3128 43 | 44 | # If you wish to use the SOCKS5 proxy feature as well, uncomment 45 | # the following option. It can be used several times 46 | # to have SOCKS5 on more than one port or on different network 47 | # interfaces (specify explicit source address for that). 48 | # 49 | # WARNING: The service accepts all requests, unless you use 50 | # SOCKS5User and make authentication mandatory. SOCKS5User 51 | # can be used repeatedly for a whole bunch of individual accounts. 52 | # 53 | #SOCKS5Proxy 8010 54 | #SOCKS5User dave:password 55 | 56 | # Use -M first to detect the best NTLM settings for your proxy. 57 | # Default is to use the only secure hash, NTLMv2, but it is not 58 | # as available as the older stuff. 59 | # 60 | # This example is the most universal setup known to man, but it 61 | # uses the weakest hash ever. I won't have it's usage on my 62 | # conscience. :) Really, try -M first. 63 | # 64 | #Auth LM 65 | #Flags 0x06820000 66 | 67 | # Enable to allow access from other computers 68 | # 69 | #Gateway yes 70 | 71 | # Useful in Gateway mode to allow/restrict certain IPs 72 | # Specifiy individual IPs or subnets one rule per line. 73 | # 74 | #Allow 127.0.0.1 75 | #Deny 0/0 76 | 77 | # GFI WebMonitor-handling plugin parameters, disabled by default 78 | # 79 | #ISAScannerSize 1024 80 | #ISAScannerAgent Wget/ 81 | #ISAScannerAgent APT-HTTP/ 82 | #ISAScannerAgent Yum/ 83 | 84 | # Headers which should be replaced if present in the request 85 | # 86 | #Header User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98) 87 | 88 | # Tunnels mapping local port to a machine behind the proxy. 89 | # The format is :: 90 | # 91 | #Tunnel 11443:remote.com:443 92 | 93 | -------------------------------------------------------------------------------- /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 | #include 27 | #include 28 | #include 29 | 30 | #include "acl.h" 31 | #include "socket.h" 32 | #include "swap.h" 33 | 34 | /* 35 | * TODO: retest ACLs on big-endian 36 | */ 37 | 38 | /* 39 | * Add the rule spec to the ACL list. 40 | */ 41 | int acl_add(plist_t *rules, char *spec, enum acl_t acl) { 42 | struct in_addr source; 43 | network_t *aux; 44 | int i, mask = 32; 45 | char *tmp; 46 | 47 | if (rules == NULL) 48 | return 0; 49 | 50 | spec = strdup(spec); 51 | aux = (network_t *)new(sizeof(network_t)); 52 | i = strcspn(spec, "/"); 53 | if (i < strlen(spec)) { 54 | spec[i] = 0; 55 | mask = strtol(spec+i+1, &tmp, 10); 56 | if (mask < 0 || mask > 32 || spec[i+1] == 0 || *tmp != 0) { 57 | syslog(LOG_ERR, "ACL netmask for %s is invalid\n", spec); 58 | free(aux); 59 | free(spec); 60 | return 0; 61 | } 62 | } 63 | 64 | if (!strcmp("*", spec)) { 65 | source.s_addr = 0; 66 | mask = 0; 67 | } else { 68 | if (!strcmp("0", spec)) { 69 | source.s_addr = 0; 70 | } else if (!so_resolv(&source, spec)) { 71 | syslog(LOG_ERR, "ACL source address %s is invalid\n", spec); 72 | free(aux); 73 | free(spec); 74 | return 0; 75 | } 76 | } 77 | 78 | aux->ip = source.s_addr; 79 | aux->mask = mask; 80 | mask = swap32(~(((uint64_t)1 << (32-mask)) - 1)); 81 | if ((source.s_addr & mask) != source.s_addr) 82 | syslog(LOG_WARNING, "Subnet definition might be incorrect: %s/%d\n", inet_ntoa(source), aux->mask); 83 | 84 | syslog(LOG_INFO, "New ACL rule: %s %s/%d\n", (acl == ACL_ALLOW ? "allow" : "deny"), inet_ntoa(source), aux->mask); 85 | *rules = plist_add(*rules, acl, (char *)aux); 86 | 87 | free(spec); 88 | return 1; 89 | } 90 | 91 | /* 92 | * Takes client IP address (network order) and walks the 93 | * ACL rules until a match is found, returning ACL_ALLOW 94 | * or ACL_DENY accordingly. If no rule matches, connection 95 | * is allowed (such is the case with no ACLs). 96 | * 97 | * Proper policy should always end with a default rule, 98 | * targetting either "*" or "0/0" to explicitly express 99 | * one's intentions. 100 | */ 101 | enum acl_t acl_check(plist_t rules, struct in_addr naddr) { 102 | network_t *aux; 103 | int mask; 104 | 105 | while (rules) { 106 | aux = (network_t *)rules->aux; 107 | mask = swap32(~(((uint64_t)1 << (32-aux->mask)) - 1)); 108 | 109 | if ((naddr.s_addr & mask) == (aux->ip & mask)) 110 | return rules->key; 111 | 112 | rules = rules->next; 113 | } 114 | 115 | return ACL_ALLOW; 116 | } 117 | -------------------------------------------------------------------------------- /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 "utils.h" 27 | #include "auth.h" 28 | 29 | struct auth_s *new_auth(void) { 30 | struct auth_s *tmp; 31 | 32 | tmp = (struct auth_s *)malloc(sizeof(struct auth_s)); 33 | if (tmp == NULL) 34 | return NULL; 35 | 36 | memset(tmp->user, 0, MINIBUF_SIZE); 37 | memset(tmp->domain, 0, MINIBUF_SIZE); 38 | memset(tmp->workstation, 0, MINIBUF_SIZE); 39 | memset(tmp->passntlm2, 0, MINIBUF_SIZE); 40 | memset(tmp->passnt, 0, MINIBUF_SIZE); 41 | memset(tmp->passlm, 0, MINIBUF_SIZE); 42 | tmp->hashntlm2 = 1; 43 | tmp->hashnt = 0; 44 | tmp->hashlm = 0; 45 | tmp->flags = 0; 46 | 47 | return tmp; 48 | } 49 | 50 | struct auth_s *copy_auth(struct auth_s *dst, struct auth_s *src, int fullcopy) { 51 | dst->hashntlm2 = src->hashntlm2; 52 | dst->hashnt = src->hashnt; 53 | dst->hashlm = src->hashlm; 54 | dst->flags = src->flags; 55 | #ifdef ENABLE_KERBEROS 56 | dst->haskrb = src->haskrb; 57 | #endif 58 | 59 | strlcpy(dst->domain, src->domain, MINIBUF_SIZE); 60 | strlcpy(dst->workstation, src->workstation, MINIBUF_SIZE); 61 | 62 | if (fullcopy) { 63 | strlcpy(dst->user, src->user, MINIBUF_SIZE); 64 | if (src->passntlm2) 65 | memcpy(dst->passntlm2, src->passntlm2, MINIBUF_SIZE); 66 | if (src->passnt) 67 | memcpy(dst->passnt, src->passnt, MINIBUF_SIZE); 68 | if (src->passlm) 69 | memcpy(dst->passlm, src->passlm, MINIBUF_SIZE); 70 | } else { 71 | memset(dst->user, 0, MINIBUF_SIZE); 72 | memset(dst->passntlm2, 0, MINIBUF_SIZE); 73 | memset(dst->passnt, 0, MINIBUF_SIZE); 74 | memset(dst->passlm, 0, MINIBUF_SIZE); 75 | } 76 | 77 | return dst; 78 | } 79 | 80 | struct auth_s *dup_auth(struct auth_s *creds, int fullcopy) { 81 | struct auth_s *tmp; 82 | 83 | tmp = new_auth(); 84 | if (tmp == NULL) 85 | return NULL; 86 | 87 | return copy_auth(tmp, creds, fullcopy); 88 | } 89 | 90 | void dump_auth(struct auth_s *creds) { 91 | char *tmp; 92 | 93 | printf("Credentials structure dump:\n"); 94 | if (creds == NULL) { 95 | printf("Struct is not allocated!\n"); 96 | return; 97 | } 98 | 99 | printf("User: %s\n", creds->user); 100 | printf("Domain: %s\n", creds->domain); 101 | printf("Wks: %s\n", creds->workstation); 102 | printf("HashNTLMv2: %d\n", creds->hashntlm2); 103 | printf("HashNT: %d\n", creds->hashnt); 104 | printf("HashLM: %d\n", creds->hashlm); 105 | printf("Flags: %X\n", creds->flags); 106 | if (creds->passntlm2) { 107 | tmp = printmem(creds->passntlm2, 16, 8); 108 | printf("PassNTLMv2: %s\n", tmp); 109 | free(tmp); 110 | } 111 | 112 | if (creds->passnt) { 113 | tmp = printmem(creds->passnt, 16, 8); 114 | printf("PassNT: %s\n", tmp); 115 | free(tmp); 116 | } 117 | 118 | if (creds->passlm) { 119 | tmp = printmem(creds->passlm, 16, 8); 120 | printf("PassLM: %s\n\n", tmp); 121 | free(tmp); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /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 "globals.h" 28 | #include "config.h" 29 | #include "utils.h" 30 | 31 | /* 32 | static const char *globals[] = { 33 | "Allow", 34 | "Deny", 35 | "Gateway", 36 | "Listen", 37 | "SOCKS5Proxy", 38 | "SOCKS5User", 39 | "NTLMToBasic", 40 | "Tunnel" }; 41 | */ 42 | 43 | config_t config_open(const char *fname) { 44 | config_t rc; 45 | FILE *fp; 46 | char *buf, *tmp, *key, *value; 47 | char section[MINIBUF_SIZE] = "global"; 48 | int i, j, slen, len, quote; 49 | 50 | //printf("sizeof = %d\n", sizeof(globals) / sizeof(char *)); 51 | 52 | fp = fopen(fname, "r"); 53 | if (!fp) 54 | return NULL; 55 | 56 | buf = new(BUFSIZE); 57 | rc = (config_t)new(sizeof(struct config_s)); 58 | rc->options = NULL; 59 | 60 | while (!feof(fp)) { 61 | quote = 0; 62 | tmp = fgets(buf, BUFSIZE, fp); 63 | if (!tmp) 64 | break; 65 | 66 | len = MIN(BUFSIZE, strlen(buf)); 67 | if (!len || feof(fp)) 68 | continue; 69 | 70 | /* 71 | * Find first non-empty character 72 | */ 73 | for (i = j = 0; j < len && isspace(buf[j]); ++j); 74 | 75 | /* 76 | * Comment? 77 | */ 78 | if (j >= len || buf[j] == '#' || buf[j] == ';') 79 | continue; 80 | 81 | /* 82 | * Find end of keyword 83 | */ 84 | for (i = j; j < len && isalnum(buf[j]); ++j); 85 | 86 | /* 87 | * Malformed? 88 | */ 89 | if (j >= len) 90 | continue; 91 | 92 | /* 93 | * Is it a section? 94 | */ 95 | if (buf[j] == '[') { 96 | for (++j; j < len && isspace(buf[j]); ++j); 97 | for (slen = j; j < len && j-slen < MINIBUF_SIZE-1 && buf[j] != ']' && !isspace(buf[j]); ++j); 98 | if (j-slen > 0) { 99 | strlcpy(section, buf+slen, j-slen+1); 100 | } 101 | continue; 102 | } 103 | 104 | /* 105 | * It's an OK keyword 106 | */ 107 | key = substr(buf, i, j-i); 108 | 109 | /* 110 | * Find next non-empty character 111 | */ 112 | for (i = j; j < len && isspace(buf[j]); ++j); 113 | if (j >= len || buf[j] == '#' || buf[j] == ';') 114 | continue; 115 | 116 | /* 117 | * Is value quoted? 118 | */ 119 | if (buf[j] == '"') { 120 | quote = 1; 121 | for (i = ++j; j < len && buf[i] != '"'; ++i); 122 | if (i >= len) 123 | continue; 124 | } else 125 | i = len; 126 | 127 | /* 128 | * Get value as quoted or until EOL/comment 129 | */ 130 | value = substr(buf, j, i-j); 131 | if (!quote) { 132 | i = strcspn(value, "#"); 133 | if (i != strlen(value)) 134 | value[i] = 0; 135 | trimr(value); 136 | } 137 | 138 | if (debug) 139 | printf("section: %s, %s = '%s'\n", section, key, value); 140 | rc->options = hlist_add(rc->options, key, value, HLIST_NOALLOC, HLIST_NOALLOC); 141 | } 142 | 143 | free(buf); 144 | fclose(fp); 145 | 146 | return rc; 147 | } 148 | 149 | void config_set(config_t cf, char *option, char *value) { 150 | cf->options = hlist_mod(cf->options, option, value, 1); 151 | } 152 | 153 | char *config_pop(config_t cf, const char *option) { 154 | char *tmp; 155 | 156 | tmp = hlist_get(cf->options, option); 157 | if (tmp) { 158 | tmp = strdup(tmp); 159 | cf->options = hlist_del(cf->options, option); 160 | } 161 | 162 | return tmp; 163 | } 164 | 165 | int config_count(config_t cf) { 166 | return hlist_count(cf->options); 167 | } 168 | 169 | void config_close(config_t cf) { 170 | if (cf == NULL) 171 | return; 172 | 173 | cf->options = hlist_free(cf->options); 174 | free(cf); 175 | } 176 | -------------------------------------------------------------------------------- /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.92.3 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 | -------------------------------------------------------------------------------- /Makefile.orig: -------------------------------------------------------------------------------- 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 | SYSCONFDIR=$(DESTDIR)/etc 7 | BINDIR=$(DESTDIR)/usr/sbin 8 | MANDIR=$(DESTDIR)/usr/share/man 9 | 10 | # 11 | # Careful now... 12 | # __BSD_VISIBLE is for FreeBSD AF_* constants 13 | # _ALL_SOURCE is for AIX 5.3 LOG_PERROR constant 14 | # 15 | NAME=cntlm 16 | CC=gcc 17 | VER=`cat VERSION` 18 | CFLAGS+=$(FLAGS) -std=c99 -Wall -Wno-unused-but-set-variable -pedantic -O3 -D__BSD_VISIBLE -D_ALL_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112 -D_ISOC99_SOURCE -D_REENTRANT -D_BSD_SOURCE -DVERSION=\"`cat VERSION`\" 19 | OS=$(shell uname -s) 20 | OSLDFLAGS=$(shell [ $(OS) = "SunOS" ] && echo "-lrt -lsocket -lnsl") 21 | LDFLAGS:=-lpthread $(OSLDFLAGS) 22 | 23 | ifeq ($(findstring CYGWIN,$(OS)),) 24 | OBJS=utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o main.o 25 | else 26 | OBJS=utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o main.o win/resources.o 27 | endif 28 | 29 | $(NAME): configure-stamp $(OBJS) 30 | @echo "Linking $@" 31 | @$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) 32 | 33 | main.o: main.c 34 | @echo "Compiling $<" 35 | @if [ -z "$(SYSCONFDIR)" ]; then \ 36 | $(CC) $(CFLAGS) -c main.c -o $@; \ 37 | else \ 38 | $(CC) $(CFLAGS) -DSYSCONFDIR=\"$(SYSCONFDIR)\" -c main.c -o $@; \ 39 | fi 40 | 41 | .c.o: 42 | @echo "Compiling $<" 43 | @$(CC) $(CFLAGS) -c -o $@ $< 44 | 45 | install: $(NAME) 46 | # Special handling for install(1) 47 | if [ "`uname -s`" = "AIX" ]; then \ 48 | install -M 755 -S -f $(BINDIR) $(NAME); \ 49 | install -M 644 -f $(MANDIR)/man1 doc/$(NAME).1; \ 50 | install -M 600 -c $(SYSCONFDIR) doc/$(NAME).conf; \ 51 | elif [ "`uname -s`" = "Darwin" ]; then \ 52 | install -d -m 755 -s $(NAME) $(BINDIR)/$(NAME); \ 53 | install -d -m 644 doc/$(NAME).1 $(MANDIR)/man1/$(NAME).1; \ 54 | [ -f $(SYSCONFDIR)/$(NAME).conf -o -z "$(SYSCONFDIR)" ] \ 55 | || install -d -m 600 doc/$(NAME).conf $(SYSCONFDIR)/$(NAME).conf; \ 56 | else \ 57 | install -D -m 755 -s $(NAME) $(BINDIR)/$(NAME); \ 58 | install -D -m 644 doc/$(NAME).1 $(MANDIR)/man1/$(NAME).1; \ 59 | [ -f $(SYSCONFDIR)/$(NAME).conf -o -z "$(SYSCONFDIR)" ] \ 60 | || install -D -m 600 doc/$(NAME).conf $(SYSCONFDIR)/$(NAME).conf; \ 61 | fi 62 | @echo; echo "Cntlm will look for configuration in $(SYSCONFDIR)/$(NAME).conf" 63 | 64 | tgz: 65 | mkdir -p tmp 66 | rm -rf tmp/$(NAME)-$(VER) 67 | svn export . tmp/$(NAME)-$(VER) 68 | tar zcvf $(NAME)-$(VER).tar.gz -C tmp/ $(NAME)-$(VER) 69 | rm -rf tmp/$(NAME)-$(VER) 70 | rmdir tmp 2>/dev/null || true 71 | 72 | tbz2: 73 | mkdir -p tmp 74 | rm -rf tmp/$(NAME)-$(VER) 75 | svn export . tmp/$(NAME)-$(VER) 76 | tar jcvf $(NAME)-$(VER).tar.bz2 -C tmp/ $(NAME)-$(VER) 77 | rm -rf tmp/$(NAME)-$(VER) 78 | rmdir tmp 2>/dev/null || true 79 | 80 | deb: builddeb 81 | builddeb: 82 | sed -i "s/^\(cntlm *\)([^)]*)/\1($(VER))/g" debian/changelog 83 | if [ `id -u` = 0 ]; then \ 84 | debian/rules binary; \ 85 | debian/rules clean; \ 86 | else \ 87 | fakeroot debian/rules binary; \ 88 | fakeroot debian/rules clean; \ 89 | fi 90 | mv ../cntlm_$(VER)*.deb . 91 | 92 | rpm: buildrpm 93 | buildrpm: 94 | sed -i "s/^\(Version:[\t ]*\)\(.*\)/\1$(VER)/g" rpm/cntlm.spec 95 | if [ `id -u` = 0 ]; then \ 96 | rpm/rules binary; \ 97 | rpm/rules clean; \ 98 | else \ 99 | fakeroot rpm/rules binary; \ 100 | fakeroot rpm/rules clean; \ 101 | fi 102 | 103 | win: buildwin 104 | buildwin: 105 | @echo 106 | @echo "* This build target must be run from a Cywgin shell on Windows *" 107 | @echo "* and you also need InnoSetup installed *" 108 | @echo 109 | rm -f win/cntlm_manual.pdf 110 | groff -t -e -mandoc -Tps doc/cntlm.1 | ps2pdf - win/cntlm_manual.pdf 111 | cat doc/cntlm.conf | unix2dos > win/cntlm.ini 112 | cat COPYRIGHT LICENSE | unix2dos > win/license.txt 113 | sed "s/\$$VERSION/$(VER)/g" win/setup.iss.in > win/setup.iss 114 | cp /bin/cygwin1.dll /bin/cyggcc_s-1.dll /bin/cygrunsrv.exe win/ 115 | cp cntlm.exe win/ 116 | strip win/cntlm.exe 117 | ln -s win $(NAME)-$(VER) 118 | zip -9 $(NAME)-$(VER).zip $(NAME)-$(VER)/cntlm.exe $(NAME)-$(VER)/cyggcc_s-1.dll $(NAME)-$(VER)/cygwin1.dll $(NAME)-$(VER)/cygrunsrv.exe $(NAME)-$(VER)/cntlm.ini $(NAME)-$(VER)/README.txt $(NAME)-$(VER)/license.txt 119 | rm -f $(NAME)-$(VER) 120 | @echo 121 | @echo Now open folder "win", right-click "setup.iss", then "Compile". 122 | @echo InnoSetup will generate a new installer cntlm-X.XX-setup.exe 123 | @echo 124 | 125 | win/resources.o: win/resources.rc 126 | @echo Adding EXE resources 127 | @windres $^ -o $@ 128 | 129 | uninstall: 130 | rm -f $(BINDIR)/$(NAME) $(MANDIR)/man1/$(NAME).1 2>/dev/null || true 131 | 132 | clean: 133 | @rm -f *.o cntlm cntlm.exe configure-stamp build-stamp config/config.h 2>/dev/null 134 | @rm -f win/*.exe win/*.dll win/*.iss win/*.pdf win/cntlm.ini win/license.txt win/resouces.o 2>/dev/null 135 | @rm -f config/endian config/gethostname config/strdup config/socklen_t config/*.exe 136 | @if [ -h Makefile ]; then rm -f Makefile; mv Makefile.gcc Makefile; fi 137 | 138 | distclean: clean 139 | if [ `id -u` = 0 ]; then \ 140 | debian/rules clean; \ 141 | rpm/rules clean; \ 142 | else \ 143 | fakeroot debian/rules clean; \ 144 | fakeroot rpm/rules clean; \ 145 | fi 146 | @rm -f *.exe *.deb *.rpm *.tgz *.tar.gz *.tar.bz2 tags ctags pid 2>/dev/null 147 | -------------------------------------------------------------------------------- /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 50 35 | #define VAL(var, type, offset) *((type *)(var+offset)) 36 | #define MEM(var, type, offset) (type *)(var+offset) 37 | 38 | #if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) 39 | # define MIN(a, b) ((a) < (b) ? (a) : (b)) 40 | #endif 41 | 42 | /* 43 | #define isalnum(c) (isalpha(c) || isdigit(c)) 44 | #define isspace(c) ((c) == ' ' || (c) == '\f' || (c) == '\t' || (c) == '\r' || (c) == '\n') 45 | */ 46 | 47 | /* 48 | * Solaris doesn't have LOG_PERROR 49 | */ 50 | #ifndef LOG_PERROR 51 | # define LOG_PERROR LOG_CONS 52 | #endif 53 | 54 | /* 55 | * Two single-linked list types. First is for storing headers, 56 | * second keeps a list of finished threads or cached connections. 57 | * Each has a different set of manipulation routines. 58 | */ 59 | typedef struct hlist_s *hlist_t; 60 | struct hlist_s { 61 | char *key; 62 | char *value; 63 | int islist; 64 | struct hlist_s *next; 65 | }; 66 | 67 | typedef struct plist_s *plist_t; 68 | struct plist_s { 69 | unsigned long key; 70 | void *aux; 71 | struct plist_s *next; 72 | }; 73 | 74 | typedef enum { 75 | HLIST_NOALLOC = 0, 76 | HLIST_ALLOC 77 | } hlist_add_t; 78 | 79 | /* 80 | * Request/response data structure. Complete and parsed req/res is 81 | * kept in this. See below for (de)allocation routines. 82 | */ 83 | typedef struct rr_data_s *rr_data_t; 84 | struct rr_data_s { 85 | int req; 86 | hlist_t headers; 87 | int code; 88 | int skip_http; 89 | int body_len; 90 | int empty; 91 | int port; 92 | char *method; 93 | char *url; 94 | char *rel_url; 95 | char *hostname; 96 | char *http; 97 | char *msg; 98 | char *body; 99 | char *errmsg; 100 | }; 101 | 102 | /* 103 | * This is used in main() for passing arguments to the thread. 104 | */ 105 | struct thread_arg_s { 106 | int fd; 107 | char *target; 108 | struct sockaddr_in addr; 109 | }; 110 | 111 | extern void myexit(int rc); 112 | extern void croak(const char *msg, int console); 113 | 114 | extern plist_t plist_add(plist_t list, unsigned long key, void *aux); 115 | extern plist_t plist_del(plist_t list, unsigned long key); 116 | extern int plist_in(plist_t list, unsigned long key); 117 | extern void plist_dump(plist_t list); 118 | extern char *plist_get(plist_t list, int key); 119 | extern int plist_pop(plist_t *list, void **aux); 120 | extern int plist_count(plist_t list); 121 | extern plist_t plist_free(plist_t list); 122 | 123 | extern hlist_t hlist_add(hlist_t list, char *key, char *value, hlist_add_t allockey, hlist_add_t allocvalue); 124 | extern hlist_t hlist_dup(hlist_t list); 125 | extern hlist_t hlist_del(hlist_t list, const char *key); 126 | extern hlist_t hlist_mod(hlist_t list, char *key, char *value, int add); 127 | extern int hlist_in(hlist_t list, const char *key); 128 | extern int hlist_count(hlist_t list); 129 | extern char *hlist_get(hlist_t list, const char *key); 130 | extern int hlist_subcmp(hlist_t list, const char *key, const char *substr); 131 | extern int hlist_subcmp_all(hlist_t list, const char *key, const char *substr); 132 | extern hlist_t hlist_free(hlist_t list); 133 | extern void hlist_dump(hlist_t list); 134 | 135 | extern char *substr(const char *src, int pos, int len); 136 | extern size_t strlcpy(char *dst, const char *src, size_t siz); 137 | extern size_t strlcat(char *dst, const char *src, size_t siz); 138 | extern char *trimr(char *buf); 139 | extern char *lowercase(char *str); 140 | extern char *uppercase(char *str); 141 | extern int unicode(char **dst, char *src); 142 | extern char *new(size_t size); 143 | extern char *urlencode(const char *str); 144 | 145 | extern rr_data_t new_rr_data(void); 146 | extern rr_data_t copy_rr_data(rr_data_t dst, rr_data_t src); 147 | extern rr_data_t dup_rr_data(rr_data_t data); 148 | extern rr_data_t reset_rr_data(rr_data_t data); 149 | extern void free_rr_data(rr_data_t data); 150 | 151 | extern char *printmem(char *src, size_t len, int bitwidth); 152 | extern char *scanmem(char *src, int bitwidth); 153 | 154 | extern void to_base64(unsigned char *out, const unsigned char *in, size_t len, size_t olen); 155 | extern int from_base64(char *out, const char *in); 156 | 157 | extern long int random(void); 158 | #if config_gethostname == 1 159 | extern int gethostname(char *name, size_t len); 160 | #endif 161 | #ifndef strdup 162 | extern char *strdup(const char *src); 163 | #endif 164 | 165 | #endif /* _UTILS_H */ 166 | -------------------------------------------------------------------------------- /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 | SYSCONFDIR=$(DESTDIR)/etc 7 | BINDIR=$(DESTDIR)/usr/sbin 8 | MANDIR=$(DESTDIR)/usr/share/man 9 | 10 | # 11 | # Careful now... 12 | # __BSD_VISIBLE is for FreeBSD AF_* constants 13 | # _ALL_SOURCE is for AIX 5.3 LOG_PERROR constant 14 | # 15 | NAME=cntlm 16 | CC=gcc 17 | VER=`cat VERSION` 18 | CFLAGS+=$(FLAGS) -std=c99 -Wall -Wno-unused-but-set-variable -pedantic -O3 -D__BSD_VISIBLE -D_ALL_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112 -D_ISOC99_SOURCE -D_REENTRANT -D_BSD_SOURCE -DVERSION=\"`cat VERSION`\" 19 | OS=$(shell uname -s) 20 | OSLDFLAGS=$(shell [ $(OS) = "SunOS" ] && echo "-lrt -lsocket -lnsl") 21 | LDFLAGS:=-lpthread $(OSLDFLAGS) 22 | 23 | ifeq ($(findstring CYGWIN,$(OS)),) 24 | OBJS=utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o main.o 25 | else 26 | OBJS=utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o main.o win/resources.o 27 | endif 28 | 29 | ENABLE_KERBEROS=$(shell grep -c ENABLE_KERBEROS config/config.h) 30 | ifeq ($(ENABLE_KERBEROS),1) 31 | OBJS+=kerberos.o 32 | LDFLAGS+=-lgssapi_krb5 33 | endif 34 | 35 | #CFLAGS+=-g 36 | 37 | all: $(NAME) 38 | 39 | $(NAME): configure-stamp $(OBJS) 40 | @echo "Linking $@" 41 | @$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) 42 | 43 | main.o: main.c 44 | @echo "Compiling $<" 45 | @if [ -z "$(SYSCONFDIR)" ]; then \ 46 | $(CC) $(CFLAGS) -c main.c -o $@; \ 47 | else \ 48 | $(CC) $(CFLAGS) -DSYSCONFDIR=\"$(SYSCONFDIR)\" -c main.c -o $@; \ 49 | fi 50 | 51 | .c.o: 52 | @echo "Compiling $<" 53 | @$(CC) $(CFLAGS) -c -o $@ $< 54 | 55 | install: $(NAME) 56 | # Special handling for install(1) 57 | if [ "`uname -s`" = "AIX" ]; then \ 58 | install -M 755 -S -f $(BINDIR) $(NAME); \ 59 | install -M 644 -f $(MANDIR)/man1 doc/$(NAME).1; \ 60 | install -M 600 -c $(SYSCONFDIR) doc/$(NAME).conf; \ 61 | elif [ "`uname -s`" = "Darwin" ]; then \ 62 | install -d -m 755 -s $(NAME) $(BINDIR)/$(NAME); \ 63 | install -d -m 644 doc/$(NAME).1 $(MANDIR)/man1/$(NAME).1; \ 64 | [ -f $(SYSCONFDIR)/$(NAME).conf -o -z "$(SYSCONFDIR)" ] \ 65 | || install -d -m 600 doc/$(NAME).conf $(SYSCONFDIR)/$(NAME).conf; \ 66 | else \ 67 | install -D -m 755 -s $(NAME) $(BINDIR)/$(NAME); \ 68 | install -D -m 644 doc/$(NAME).1 $(MANDIR)/man1/$(NAME).1; \ 69 | [ -f $(SYSCONFDIR)/$(NAME).conf -o -z "$(SYSCONFDIR)" ] \ 70 | || install -D -m 600 doc/$(NAME).conf $(SYSCONFDIR)/$(NAME).conf; \ 71 | fi 72 | @echo; echo "Cntlm will look for configuration in $(SYSCONFDIR)/$(NAME).conf" 73 | 74 | tgz: 75 | mkdir -p tmp 76 | rm -rf tmp/$(NAME)-$(VER) 77 | svn export . tmp/$(NAME)-$(VER) 78 | tar zcvf $(NAME)-$(VER).tar.gz -C tmp/ $(NAME)-$(VER) 79 | rm -rf tmp/$(NAME)-$(VER) 80 | rmdir tmp 2>/dev/null || true 81 | 82 | tbz2: 83 | mkdir -p tmp 84 | rm -rf tmp/$(NAME)-$(VER) 85 | svn export . tmp/$(NAME)-$(VER) 86 | tar jcvf $(NAME)-$(VER).tar.bz2 -C tmp/ $(NAME)-$(VER) 87 | rm -rf tmp/$(NAME)-$(VER) 88 | rmdir tmp 2>/dev/null || true 89 | 90 | deb: builddeb 91 | builddeb: 92 | sed -i "s/^\(cntlm *\)([^)]*)/\1($(VER))/g" debian/changelog 93 | if [ `id -u` = 0 ]; then \ 94 | debian/rules binary; \ 95 | debian/rules clean; \ 96 | else \ 97 | fakeroot debian/rules binary; \ 98 | fakeroot debian/rules clean; \ 99 | fi 100 | mv ../cntlm_$(VER)*.deb . 101 | 102 | rpm: buildrpm 103 | buildrpm: 104 | sed -i "s/^\(Version:[\t ]*\)\(.*\)/\1$(VER)/g" rpm/cntlm.spec 105 | if [ `id -u` = 0 ]; then \ 106 | rpm/rules binary; \ 107 | rpm/rules clean; \ 108 | else \ 109 | fakeroot rpm/rules binary; \ 110 | fakeroot rpm/rules clean; \ 111 | fi 112 | 113 | win: buildwin 114 | buildwin: 115 | @echo 116 | @echo "* This build target must be run from a Cywgin shell on Windows *" 117 | @echo "* and you also need InnoSetup installed *" 118 | @echo 119 | rm -f win/cntlm_manual.pdf 120 | groff -t -e -mandoc -Tps doc/cntlm.1 | ps2pdf - win/cntlm_manual.pdf 121 | cat doc/cntlm.conf | unix2dos > win/cntlm.ini 122 | cat COPYRIGHT LICENSE | unix2dos > win/license.txt 123 | sed "s/\$$VERSION/$(VER)/g" win/setup.iss.in > win/setup.iss 124 | cp /bin/cygwin1.dll /bin/cyggcc_s-1.dll /bin/cygrunsrv.exe win/ 125 | cp cntlm.exe win/ 126 | strip win/cntlm.exe 127 | ln -s win $(NAME)-$(VER) 128 | zip -9 $(NAME)-$(VER).zip $(NAME)-$(VER)/cntlm.exe $(NAME)-$(VER)/cyggcc_s-1.dll $(NAME)-$(VER)/cygwin1.dll $(NAME)-$(VER)/cygrunsrv.exe $(NAME)-$(VER)/cntlm.ini $(NAME)-$(VER)/README.txt $(NAME)-$(VER)/license.txt 129 | rm -f $(NAME)-$(VER) 130 | @echo 131 | @echo Now open folder "win", right-click "setup.iss", then "Compile". 132 | @echo InnoSetup will generate a new installer cntlm-X.XX-setup.exe 133 | @echo 134 | 135 | win/resources.o: win/resources.rc 136 | @echo Adding EXE resources 137 | @windres $^ -o $@ 138 | 139 | uninstall: 140 | rm -f $(BINDIR)/$(NAME) $(MANDIR)/man1/$(NAME).1 2>/dev/null || true 141 | 142 | clean: 143 | @rm -f *.o cntlm cntlm.exe configure-stamp build-stamp config/config.h 2>/dev/null 144 | @rm -f win/*.exe win/*.dll win/*.iss win/*.pdf win/cntlm.ini win/license.txt win/resouces.o 2>/dev/null 145 | @rm -f config/endian config/gethostname config/strdup config/socklen_t config/*.exe 146 | @if [ -h Makefile ]; then rm -f Makefile; mv Makefile.gcc Makefile; fi 147 | 148 | distclean: clean 149 | if [ `id -u` = 0 ]; then \ 150 | debian/rules clean; \ 151 | rpm/rules clean; \ 152 | else \ 153 | fakeroot debian/rules clean; \ 154 | fakeroot rpm/rules clean; \ 155 | fi 156 | @rm -f *.exe *.deb *.rpm *.tgz *.tar.gz *.tar.bz2 tags ctags pid 2>/dev/null 157 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Installation using packages 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | Most of the popular distros contain cntlm packages.n 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 | Traditional compilation steps: 83 | 84 | $ ./configure 85 | $ make 86 | 87 | Prepare all binaries, manuals, config templates, Start Menu links and InnoSetup 88 | project definition file: 89 | 90 | $ make win 91 | 92 | Then run InnoSetup compiler to pack it all into an automatic installer EXE: 93 | 94 | $ /your/path/to/ISCC.exe win/setup.iss 95 | or 96 | Open folder "win" in explorer, right click "setup.iss" and select "Compile". 97 | 98 | Both with generate an installer in the "win" folder. 99 | 100 | Traditional installation 101 | ~~~~~~~~~~~~~~~~~~~~~~~~ 102 | First, you have to compile cntlm. Using the Makefile, this should be very easy: 103 | 104 | $ ./configure 105 | $ make 106 | $ make install 107 | 108 | Cntlm does not require any dynamic libraries and there are no dependencies you 109 | have to satisfy before compilation, except for libpthreads. This library is 110 | required for all threaded applications and is very likely to be part of your 111 | system already, because it comes with libc. Next, install cntlm onto your 112 | system like so: 113 | 114 | Default installation directories are /usr/sbin, /usr/share/man and /etc. Should 115 | you want to install cntlm into a different location, change the DESTDIR 116 | installation prefix (from "/") to add a different installation prefix (e.g. 117 | /usr/local). To change individual directories, use BINDIR, MANDIR and 118 | SYSCONFDIR: 119 | 120 | $ make SYSCONFDIR=/etc BINDIR=/usr/bin MANDIR=/usr/share/man 121 | $ make install SYSCONFDIR=/etc BINDIR=/usr/bin MANDIR=/usr/share/man 122 | 123 | Cntlm is compiled with system-wide configuration file by default. That means 124 | whenever you run cntlm, it looks into a hardcoded path (SYSCONFDIR) and tries 125 | to load cntml.conf. You cannot make it not to do so, unless you use -c with an 126 | alternative file or /dev/null. This is standard behaviour and probably what you 127 | want. On the other hand, some of you might not want to use cntlm as a daemon 128 | started by init scripts and you would prefer setting up everything on the 129 | command line. This is possible, just comment out SYSCONFDIR variable definition 130 | in the Makefile before you compile cntlm and it will remove this feature. 131 | 132 | Installation includes the main binary, the man page (see "man cntlm") and if 133 | the default config feature was not removed, it also installs a configuration 134 | template. Please note that unlike bin and man targets, existing configuration 135 | is never overwritten during installation. In the doc/ directory you can find 136 | among other things a file called "cntlmd". It can be used as an init.d script. 137 | 138 | 139 | Architectures 140 | ~~~~~~~~~~~~~ 141 | The build system now has an autodetection of the build arch endianness. Every 142 | common CPU and OS out there is supported, including Windows, MacOS X, Linux, 143 | *BSD, AIX. 144 | 145 | 146 | Compilers 147 | ~~~~~~~~~ 148 | Cntlm is tested against GCC and IBM XL C/C++, other C compilers will work 149 | for you too. There are no compiler specific directives and options AFAIK. 150 | compilers might work for you (then again, they might not). Specific 151 | Makefiles for different compilers are supported by the ./configure script 152 | (e.g. Makefile.xlc) 153 | 154 | 155 | Contact 156 | ~~~~~~~ 157 | David Kubicek 158 | -------------------------------------------------------------------------------- /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 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "utils.h" 37 | 38 | extern int debug; 39 | 40 | /* 41 | * gethostbyname() wrapper. Return 1 if OK, otherwise 0. 42 | */ 43 | int so_resolv(struct in_addr *host, const char *name) { 44 | /* 45 | struct hostent *resolv; 46 | 47 | resolv = gethostbyname(name); 48 | if (!resolv) 49 | return 0; 50 | 51 | memcpy(host, resolv->h_addr_list[0], resolv->h_length); 52 | return 1; 53 | */ 54 | struct addrinfo hints, *res, *p; 55 | 56 | memset(&hints, 0, sizeof(hints)); 57 | hints.ai_family = AF_INET; 58 | hints.ai_socktype = SOCK_STREAM; 59 | int rc = getaddrinfo(name, NULL, &hints, &res); 60 | if (rc != 0) { 61 | if (debug) 62 | printf("so_resolv: %s failed: %s (%d)\n", name, gai_strerror(rc), rc); 63 | return 0; 64 | } 65 | 66 | if (debug) 67 | printf("Resolve %s:\n", name); 68 | int addr_set = 0; 69 | for (p = res; p != NULL; p = p->ai_next) { 70 | struct sockaddr_in *ad = (struct sockaddr_in*)(p->ai_addr); 71 | if (ad == NULL) { 72 | freeaddrinfo(res); 73 | return 0; 74 | } 75 | if (!addr_set) { 76 | memcpy(host, &ad->sin_addr, sizeof(ad->sin_addr)); 77 | addr_set = 1; 78 | if (debug) 79 | printf(" -> %s\n", inet_ntoa(ad->sin_addr)); 80 | } else 81 | if (debug) 82 | printf(" %s\n", inet_ntoa(ad->sin_addr)); 83 | } 84 | 85 | freeaddrinfo(res); 86 | 87 | return 1; 88 | } 89 | 90 | /* 91 | * Connect to a host. Host is required to be resolved 92 | * in the struct in_addr already. 93 | * Returns: socket descriptor 94 | */ 95 | int so_connect(struct in_addr host, int port) { 96 | int flags, fd, rc; 97 | struct sockaddr_in saddr; 98 | // struct timeval tv; 99 | // fd_set fds; 100 | 101 | if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { 102 | if (debug) 103 | printf("so_connect: create: %s\n", strerror(errno)); 104 | return -1; 105 | } 106 | 107 | memset(&saddr, 0, sizeof(saddr)); 108 | saddr.sin_family = AF_INET; 109 | saddr.sin_port = htons(port); 110 | saddr.sin_addr = host; 111 | 112 | if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { 113 | if (debug) 114 | printf("so_connect: get flags: %s\n", strerror(errno)); 115 | close(fd); 116 | return -1; 117 | } 118 | 119 | /* NON-BLOCKING connect with timeout 120 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { 121 | if (debug) 122 | printf("so_connect: set non-blocking: %s\n", strerror(errno)); 123 | close(fd); 124 | return -1; 125 | } 126 | */ 127 | 128 | rc = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); 129 | 130 | /* 131 | printf("connect = %d\n", rc); 132 | if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS)) { 133 | FD_ZERO(&fds); 134 | FD_SET(fd, &fds); 135 | tv.tv_sec = 10; 136 | tv.tv_usec = 0; 137 | printf("select!\n"); 138 | rc = select(fd+1, NULL, &fds, NULL, &tv) - 1; 139 | printf("select = %d\n", rc); 140 | } 141 | */ 142 | 143 | if (rc < 0) { 144 | if (debug) 145 | printf("so_connect: %s\n", strerror(errno)); 146 | close(fd); 147 | return -1; 148 | } 149 | 150 | if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { 151 | if (debug) 152 | printf("so_connect: set blocking: %s\n", strerror(errno)); 153 | close(fd); 154 | return -1; 155 | } 156 | 157 | return fd; 158 | } 159 | 160 | /* 161 | * Bind the specified port and listen on it. 162 | * Return socket descriptor if OK, otherwise 0. 163 | */ 164 | int so_listen(int port, struct in_addr source) { 165 | struct sockaddr_in saddr; 166 | int fd; 167 | socklen_t clen; 168 | 169 | fd = socket(PF_INET, SOCK_STREAM, 0); 170 | if (fd < 0) { 171 | if (debug) 172 | printf("so_listen: new socket: %s\n", strerror(errno)); 173 | close(fd); 174 | return -1; 175 | } 176 | 177 | clen = 1; 178 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &clen, sizeof(clen)); 179 | memset((void *)&saddr, 0, sizeof(saddr)); 180 | saddr.sin_family = AF_INET; 181 | saddr.sin_port = htons(port); 182 | saddr.sin_addr.s_addr = source.s_addr; 183 | 184 | if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr))) { 185 | syslog(LOG_ERR, "Cannot bind port %d: %s!\n", port, strerror(errno)); 186 | close(fd); 187 | return -1; 188 | } 189 | 190 | if (listen(fd, 5)) { 191 | close(fd); 192 | return -1; 193 | } 194 | 195 | return fd; 196 | } 197 | 198 | /* 199 | * Return 1 if data is available on the socket, 200 | * 0 if connection was closed 201 | * -1 if error (errno is set) 202 | */ 203 | int so_recvtest(int fd) { 204 | char buf; 205 | int i; 206 | #ifndef MSG_DONTWAIT 207 | unsigned int flags; 208 | 209 | flags = fcntl(fd, F_GETFL); 210 | fcntl(fd, F_SETFL, flags | O_NONBLOCK); 211 | i = recv(fd, &buf, 1, MSG_PEEK); 212 | fcntl(fd, F_SETFL, flags); 213 | #else 214 | i = recv(fd, &buf, 1, MSG_DONTWAIT | MSG_PEEK); 215 | #endif 216 | 217 | return i; 218 | } 219 | 220 | /* 221 | * Return true if there are some data on the socket 222 | */ 223 | int so_dataready(int fd) { 224 | return so_recvtest(fd) > 0; 225 | } 226 | 227 | /* 228 | * Reliable way of finding out whether a connection was closed 229 | * on the remote end, without actually reading from it. 230 | */ 231 | int so_closed(int fd) { 232 | int i; 233 | 234 | if (fd == -1) 235 | return 1; 236 | 237 | i = so_recvtest(fd); 238 | return (i == 0 || (i == -1 && errno != EAGAIN && errno != ENOENT)); /* ENOENT, you ask? Perhap AIX devels could explain! :-( */ 239 | } 240 | 241 | /* 242 | * Receive a single line from the socket. This is no super-efficient 243 | * implementation, but more than we need to read in a few headers. 244 | * What's more, the data is actually recv'd from a socket buffer. 245 | * 246 | * I had to time this in comparison to recv with block read :) and 247 | * the performance was very similar. Given the fact that it keeps us 248 | * from creating a whole buffering scheme around the socket (HTTP 249 | * connection is both line and block oriented, switching back and forth), 250 | * it is actually OK. 251 | */ 252 | int so_recvln(int fd, char **buf, int *size) { 253 | int len = 0; 254 | int r = 1; 255 | char c = 0; 256 | char *tmp; 257 | 258 | while (len < *size-1 && c != '\n') { 259 | r = read(fd, &c, 1); 260 | if (r <= 0) 261 | break; 262 | 263 | (*buf)[len++] = c; 264 | 265 | /* 266 | * End of buffer, still no EOL? Resize the buffer 267 | */ 268 | if (len == *size-1 && c != '\n') { 269 | if (debug) 270 | printf("so_recvln(%d): realloc %d\n", fd, *size*2); 271 | *size *= 2; 272 | tmp = realloc(*buf, *size); 273 | if (tmp == NULL) 274 | return -1; 275 | else 276 | *buf = tmp; 277 | } 278 | } 279 | (*buf)[len] = 0; 280 | 281 | return r; 282 | } 283 | 284 | -------------------------------------------------------------------------------- /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 | #include 28 | #include 29 | 30 | #include "utils.h" 31 | #include "socket.h" 32 | #include "http.h" 33 | #include "globals.h" 34 | #include "forward.h" 35 | #include "scanner.h" 36 | 37 | /* 38 | * This code is a piece of shit, but it works. Cannot rewrite it now, because 39 | * I don't have ISA AV filter anymore - wouldn't be able to test it. 40 | */ 41 | int scanner_hook(rr_data_t request, rr_data_t response, struct auth_s *credentials, int cd, int *sd, long maxKBs) { 42 | char *buf, *line, *pos, *tmp, *pat, *post, *isaid, *uurl; 43 | int bsize, lsize, size, len, i, w, nc; 44 | rr_data_t newreq, newres; 45 | plist_t list; 46 | 47 | int ok = 1; 48 | int done = 0; 49 | int headers_initiated = 0; 50 | long c, progress = 0, filesize = 0; 51 | 52 | /* 53 | * Let's limit the responses we examine to an absolute minimum 54 | */ 55 | if (!request->req || response->code != 200 56 | || http_has_body(request, response) != -1 57 | || hlist_subcmp(response->headers, "Transfer-Encoding", "chunked") 58 | || !hlist_subcmp(response->headers, "Proxy-Connection", "close")) 59 | return PLUG_SENDHEAD | PLUG_SENDDATA; 60 | 61 | tmp = hlist_get(request->headers, "User-Agent"); 62 | if (tmp) { 63 | tmp = lowercase(strdup(tmp)); 64 | list = scanner_agent_list; 65 | while (list) { 66 | pat = lowercase(strdup(list->aux)); 67 | if (debug) 68 | printf("scanner_hook: matching U-A header (%s) to %s\n", tmp, pat); 69 | if (!fnmatch(pat, tmp, 0)) { 70 | if (debug) 71 | printf("scanner_hook: positive match!\n"); 72 | maxKBs = 0; 73 | free(pat); 74 | break; 75 | } 76 | free(pat); 77 | list = list->next; 78 | } 79 | free(tmp); 80 | } 81 | 82 | bsize = SAMPLE; 83 | buf = new(bsize); 84 | 85 | len = 0; 86 | do { 87 | size = read(*sd, buf + len, SAMPLE - len - 1); 88 | if (debug) 89 | printf("scanner_hook: read %d of %d\n", size, SAMPLE - len); 90 | if (size > 0) 91 | len += size; 92 | } while (size > 0 && len < SAMPLE - 1); 93 | 94 | if (strstr(buf, "Downloading status") && (pos=strstr(buf, "ISAServerUniqueID=")) && (pos = strchr(pos, '"'))) { 95 | pos++; 96 | c = strlen(pos); 97 | for (i = 0; i < c && pos[i] != '"'; ++i); 98 | 99 | if (pos[i] == '"') { 100 | isaid = substr(pos, 0, i); 101 | if (debug) 102 | printf("scanner_hook: ISA id = %s\n", isaid); 103 | 104 | lsize = BUFSIZE; 105 | line = new(lsize); 106 | do { 107 | i = so_recvln(*sd, &line, &lsize); 108 | 109 | c = strlen(line); 110 | if (len + c >= bsize) { 111 | bsize *= 2; 112 | tmp = realloc(buf, bsize); 113 | if (tmp == NULL) 114 | break; 115 | else 116 | buf = tmp; 117 | } 118 | 119 | strcat(buf, line); 120 | len += c; 121 | 122 | if (i >= 0 && ( 123 | ((pos = strstr(line, "UpdatePage(")) 124 | && isdigit(pos[11])) 125 | || 126 | ((pos = strstr(line, "DownloadFinished(")) 127 | && isdigit(pos[17]) 128 | && (done = 1)) )) { 129 | if (debug) 130 | printf("scanner_hook: %s", line); 131 | 132 | if ((pos = strstr(line, "To be downloaded"))) { 133 | filesize = atol(pos+16); 134 | if (debug) { 135 | if (filesize > 0) { 136 | printf("scanner_hook: file size detected: %ld KiBs (max: %ld)\n", filesize/1024, maxKBs); 137 | } else { 138 | printf("scanner_hook: file size unknown -- quitting\n"); 139 | break; 140 | } 141 | } 142 | 143 | if (maxKBs && (maxKBs == 1 || filesize/1024 > maxKBs)) 144 | break; 145 | 146 | /* 147 | * We have to send HTTP protocol ID so we can send the notification 148 | * headers during downloading. Once we've done that, it cannot appear 149 | * again, which it would if we returned PLUG_SENDHEAD, so we must 150 | * remember to not include it. 151 | */ 152 | headers_initiated = 1; 153 | tmp = new(MINIBUF_SIZE); 154 | snprintf(tmp, MINIBUF_SIZE, "%s 200 OK\r\n", request->http); 155 | w = write(cd, tmp, strlen(tmp)); 156 | free(tmp); 157 | } 158 | 159 | if (!headers_initiated) { 160 | if (debug) 161 | printf("scanner_hook: Giving up, \"To be downloaded\" line not found!\n"); 162 | break; 163 | } 164 | 165 | /* 166 | * Send a notification header to the client, just so it doesn't timeout 167 | */ 168 | if (!done) { 169 | tmp = new(MINIBUF_SIZE); 170 | progress = atol(line+12); 171 | snprintf(tmp, MINIBUF_SIZE, "ISA-Scanner: %ld of %ld\r\n", progress, filesize); 172 | w = write(cd, tmp, strlen(tmp)); 173 | free(tmp); 174 | } 175 | 176 | /* 177 | * If download size is unknown beforehand, stop when downloaded amount is over ISAScannerSize 178 | */ 179 | if (!filesize && maxKBs && maxKBs != 1 && progress/1024 > maxKBs) 180 | break; 181 | } 182 | } while (i > 0 && !done); 183 | 184 | if (i >= 0 && done && (pos = strstr(line, "\",\"")+3) && (c = strchr(pos, '"') - pos) > 0) { 185 | tmp = substr(pos, 0, c); 186 | pos = urlencode(tmp); 187 | free(tmp); 188 | 189 | uurl = urlencode(request->url); 190 | 191 | post = new(BUFSIZE); 192 | snprintf(post, BUFSIZE-1, "%surl=%s&%sSaveToDisk=YES&%sOrig=%s", isaid, pos, isaid, isaid, uurl); 193 | 194 | if (debug) 195 | printf("scanner_hook: Getting file with URL data = %s\n", request->url); 196 | 197 | tmp = new(MINIBUF_SIZE); 198 | snprintf(tmp, MINIBUF_SIZE, "%d", (int)strlen(post)); 199 | 200 | newres = new_rr_data(); 201 | newreq = dup_rr_data(request); 202 | 203 | free(newreq->method); 204 | newreq->method = strdup("POST"); 205 | hlist_mod(newreq->headers, "Referer", request->url, 1); 206 | hlist_mod(newreq->headers, "Content-Type", "application/x-www-form-urlencoded", 1); 207 | hlist_mod(newreq->headers, "Content-Length", tmp, 1); 208 | free(tmp); 209 | 210 | nc = proxy_connect(credentials); 211 | c = proxy_authenticate(&nc, newreq, newres, credentials); 212 | if (c && newres->code == 407) { 213 | if (debug) 214 | printf("scanner_hook: Authentication OK, getting the file...\n"); 215 | } else { 216 | if (debug) 217 | printf("scanner_hook: Authentication failed or refused!\n"); 218 | close(nc); 219 | nc = 0; 220 | } 221 | 222 | /* 223 | * The POST request for the real file 224 | */ 225 | reset_rr_data(newres); 226 | if (nc && headers_send(nc, newreq) && write(nc, post, strlen(post)) && headers_recv(nc, newres)) { 227 | if (debug) 228 | hlist_dump(newres->headers); 229 | 230 | /* 231 | * We always know the filesize here. Send it to the client, because ISA doesn't!!! 232 | * The clients progress bar doesn't work without it and it stinks! 233 | */ 234 | if (filesize || progress) { 235 | tmp = new(20); 236 | snprintf(tmp, 20, "%ld", filesize ? filesize : progress); 237 | newres->headers = hlist_mod(newres->headers, "Content-Length", tmp, 1); 238 | } 239 | 240 | /* 241 | * Here we remember if previous code already sent some headers 242 | * to the client. In such case, do not include the HTTP/1.x ID. 243 | */ 244 | newres->skip_http = headers_initiated; 245 | copy_rr_data(response, newres); 246 | close(*sd); 247 | *sd = nc; 248 | 249 | len = 0; 250 | ok = PLUG_SENDHEAD | PLUG_SENDDATA; 251 | } else if (debug) 252 | printf("scanner_hook: New request failed\n"); 253 | 254 | free(newreq); 255 | free(newres); 256 | free(post); 257 | free(uurl); 258 | } 259 | 260 | free(line); 261 | free(isaid); 262 | } else if (debug) 263 | printf("scanner_hook: ISA id not found\n"); 264 | } 265 | 266 | if (len) { 267 | if (debug) { 268 | printf("scanner_hook: flushing %d original bytes\n", len); 269 | hlist_dump(response->headers); 270 | } 271 | 272 | if (!headers_send(cd, response)) { 273 | if (debug) 274 | printf("scanner_hook: failed to send headers\n"); 275 | free(buf); 276 | return PLUG_ERROR; 277 | } 278 | 279 | size = write(cd, buf, len); 280 | if (size > 0) 281 | ok = PLUG_SENDDATA; 282 | else 283 | ok = PLUG_ERROR; 284 | } 285 | 286 | if (debug) 287 | printf("scanner_hook: ending with %d\n", ok); 288 | 289 | free(buf); 290 | return ok; 291 | } 292 | 293 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 "globals.h" 44 | #include "auth.h" 45 | #include "kerberos.h" 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | /* 54 | * Function: display_ctx_flags 55 | * 56 | * Purpose: displays the flags returned by context initiation in 57 | * a human-readable form 58 | * 59 | * Arguments: 60 | * 61 | * int ret_flags 62 | * 63 | * Effects: 64 | * 65 | * Strings corresponding to the context flags are printed on 66 | * stdout, preceded by "context flag: " and followed by a newline 67 | */ 68 | 69 | void display_ctx_flags(OM_uint32 flags) { 70 | if (flags & GSS_C_DELEG_FLAG) 71 | syslog(LOG_INFO, "context flag: GSS_C_DELEG_FLAG\n"); 72 | if (flags & GSS_C_MUTUAL_FLAG) 73 | syslog(LOG_INFO, "context flag: GSS_C_MUTUAL_FLAG\n"); 74 | if (flags & GSS_C_REPLAY_FLAG) 75 | syslog(LOG_INFO, "context flag: GSS_C_REPLAY_FLAG\n"); 76 | if (flags & GSS_C_SEQUENCE_FLAG) 77 | syslog(LOG_INFO, "context flag: GSS_C_SEQUENCE_FLAG\n"); 78 | if (flags & GSS_C_CONF_FLAG) 79 | syslog(LOG_INFO, "context flag: GSS_C_CONF_FLAG\n"); 80 | if (flags & GSS_C_INTEG_FLAG) 81 | syslog(LOG_INFO, "context flag: GSS_C_INTEG_FLAG\n"); 82 | } 83 | 84 | static void display_status_1(char *m, OM_uint32 code, int type) { 85 | OM_uint32 maj_stat, min_stat; 86 | gss_buffer_desc msg; 87 | OM_uint32 msg_ctx; 88 | 89 | msg_ctx = 0; 90 | while (1) { 91 | maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID, 92 | &msg_ctx, &msg); 93 | if (1) 94 | syslog(LOG_ERR, "GSS-API error %s: %s\n", m, (char *) msg.value); 95 | (void) gss_release_buffer(&min_stat, &msg); 96 | 97 | if (!msg_ctx) 98 | break; 99 | } 100 | } 101 | 102 | /* 103 | * Function: display_status 104 | * 105 | * Purpose: displays GSS-API messages 106 | * 107 | * Arguments: 108 | * 109 | * msg a string to be displayed with the message 110 | * maj_stat the GSS-API major status code 111 | * min_stat the GSS-API minor status code 112 | * 113 | * Effects: 114 | * 115 | * The GSS-API messages associated with maj_stat and min_stat are 116 | * displayed on stderr, each preceded by "GSS-API error : " and 117 | * followed by a newline. 118 | */ 119 | void display_status(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) { 120 | display_status_1(msg, maj_stat, GSS_C_GSS_CODE); 121 | if (maj_stat != GSS_S_COMPLETE) 122 | display_status_1(msg, min_stat, GSS_C_MECH_CODE); 123 | } 124 | 125 | void display_name(char* txt, gss_name_t *name) { 126 | gss_OID mechOid = GSS_C_NO_OID; 127 | OM_uint32 maj_stat; 128 | OM_uint32 min_stat; 129 | gss_buffer_desc out_name; 130 | 131 | // maj_stat = gss_display_name(&min_stat, *name, &out_name, &mechOid); 132 | maj_stat = gss_display_name(&min_stat, *name, &out_name, NULL); 133 | if (maj_stat != GSS_S_COMPLETE) { 134 | display_status("Display name", maj_stat, min_stat); 135 | } 136 | 137 | syslog(LOG_INFO, txt, (char *) out_name.value); 138 | 139 | (void) gss_release_buffer(&min_stat, &out_name); 140 | 141 | if (mechOid != GSS_C_NO_OID) 142 | (void) gss_release_oid(&min_stat, &mechOid); 143 | } 144 | 145 | int acquire_name(gss_name_t *target_name, char *service_name, gss_OID oid) { 146 | gss_buffer_desc tmp_tok; 147 | OM_uint32 maj_stat, min_stat; 148 | 149 | tmp_tok.value = service_name; 150 | tmp_tok.length = strlen(service_name) + 1; 151 | 152 | maj_stat = gss_import_name(&min_stat, &tmp_tok, oid, target_name); 153 | 154 | if (maj_stat != GSS_S_COMPLETE) { 155 | display_status("Parsing name", maj_stat, min_stat); 156 | } else if (debug){ 157 | display_name("Acquired kerberos name %s\n", target_name); 158 | } 159 | return maj_stat; 160 | } 161 | 162 | /* 163 | * Function: client_establish_context 164 | * 165 | * Purpose: establishes a GSS-API context with a specified service and 166 | * returns the context handle 167 | * 168 | * Arguments: 169 | * 170 | * service_name (r) the ASCII service name of the service 171 | * context (w) the established GSS-API context 172 | * ret_flags (w) the returned flags from init_sec_context 173 | * 174 | * Returns: 0 on success, -1 on failure 175 | * 176 | * Effects: 177 | * 178 | * service_name is imported as a GSS-API name and a GSS-API context is 179 | * established with the corresponding service; the service should be 180 | * listening on the TCP connection s. The default GSS-API mechanism 181 | * is used, and mutual authentication and replay detection are 182 | * requested. 183 | * 184 | * If successful, the context handle is returned in context. If 185 | * unsuccessful, the GSS-API error messages are displayed on stderr 186 | * and -1 is returned. 187 | */ 188 | int client_establish_context(char *service_name, 189 | OM_uint32 *ret_flags, gss_buffer_desc* send_tok) { 190 | gss_name_t target_name; 191 | gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; 192 | OM_uint32 maj_stat, min_stat, init_min_stat; 193 | 194 | if ((maj_stat = acquire_name(&target_name, service_name, 195 | GSS_C_NT_HOSTBASED_SERVICE)) != GSS_S_COMPLETE) 196 | return maj_stat; 197 | 198 | if (debug) 199 | display_name("SPN name %s\n", &target_name); 200 | 201 | maj_stat = gss_init_sec_context(&init_min_stat, GSS_C_NO_CREDENTIAL, 202 | &gss_context, 203 | target_name, 204 | GSS_C_NULL_OID,// use default mech 205 | 0, 0, // no special flags requested, no time req 206 | GSS_C_NO_CHANNEL_BINDINGS, /* no channel bindings */ 207 | GSS_C_NO_BUFFER, // no input buffer 208 | NULL, /* ignore mech type */ 209 | send_tok, ret_flags, //the returned token, the token flags 210 | NULL /* ignore time_rec */ 211 | ); 212 | 213 | gss_release_name(&min_stat, &target_name); 214 | 215 | if (maj_stat != GSS_S_COMPLETE) { 216 | if(maj_stat == GSS_S_CONTINUE_NEEDED){ 217 | //TODO 218 | } 219 | display_status("Initializing context", maj_stat, init_min_stat); 220 | 221 | if (gss_context == GSS_C_NO_CONTEXT) 222 | gss_delete_sec_context(&min_stat, &gss_context, GSS_C_NO_BUFFER); 223 | return maj_stat; 224 | } 225 | 226 | if (debug) 227 | syslog(LOG_INFO, "Got token (size=%d)\n", (int) send_tok->length); 228 | 229 | maj_stat = gss_delete_sec_context(&min_stat, &gss_context, GSS_C_NO_BUFFER); 230 | if (maj_stat != GSS_S_COMPLETE) { 231 | display_status("Deleting context", maj_stat, min_stat); 232 | } 233 | return GSS_S_COMPLETE;//maj_stat; 234 | } 235 | 236 | 237 | 238 | /** 239 | * acquires a kerberos token for default credential using SPN HTTP@ 240 | */ 241 | int acquire_kerberos_token(proxy_t* proxy, struct auth_s *credentials, 242 | char* buf) { 243 | char service_name[BUFSIZE], token[BUFSIZE]; 244 | OM_uint32 ret_flags, min_stat; 245 | 246 | if (credentials->haskrb == KRB_KO) { 247 | if (debug) 248 | syslog(LOG_INFO, "Skipping already failed gss auth for %s\n", 249 | proxy->hostname); 250 | return 0; 251 | } 252 | 253 | if (!(credentials->haskrb & KRB_CREDENTIAL_AVAILABLE)) { 254 | //try to get credential 255 | // if(acquire_credential(credentials)){ 256 | credentials->haskrb |= check_credential(); 257 | if (!(credentials->haskrb & KRB_CREDENTIAL_AVAILABLE)){ 258 | //no credential -> no token 259 | if (debug) 260 | syslog(LOG_INFO, "No valid credential available\n"); 261 | return 0; 262 | } 263 | // } 264 | } 265 | 266 | gss_buffer_desc send_tok; 267 | 268 | strcpy(service_name, "HTTP@"); 269 | strcat(service_name, proxy->hostname); 270 | 271 | int rc = client_establish_context(service_name, &ret_flags, &send_tok); 272 | 273 | if (rc == GSS_S_COMPLETE) { 274 | credentials->haskrb = KRB_OK; 275 | 276 | to_base64((unsigned char *) token, send_tok.value, send_tok.length, 277 | BUFSIZE); 278 | 279 | if (debug) { 280 | syslog(LOG_INFO, "Token B64 (size=%d)... %s\n", 281 | (int) strlen(token), token); 282 | display_ctx_flags(ret_flags); 283 | } 284 | 285 | strcpy(buf, "NEGOTIATE "); 286 | strcat(buf, token); 287 | 288 | rc=1; 289 | } else { 290 | credentials->haskrb = KRB_KO; 291 | 292 | if (debug) 293 | syslog(LOG_INFO, "No valid token acquired for %s\n", service_name); 294 | 295 | rc=0; 296 | } 297 | 298 | (void) gss_release_buffer(&min_stat, &send_tok); 299 | 300 | return rc; 301 | } 302 | 303 | /** 304 | * checks if a default cached credential is cached 305 | */ 306 | int check_credential() { 307 | OM_uint32 min_stat; 308 | gss_name_t name; 309 | OM_uint32 lifetime; 310 | gss_cred_usage_t cred_usage; 311 | gss_OID_set mechanisms; 312 | OM_uint32 maj_stat; 313 | 314 | maj_stat = gss_inquire_cred(&min_stat, GSS_C_NO_CREDENTIAL, &name, 315 | &lifetime, &cred_usage, &mechanisms); 316 | if (maj_stat != GSS_S_COMPLETE) { 317 | display_status("Inquire credential", maj_stat, min_stat); 318 | return 0; 319 | } 320 | (void) gss_release_oid_set(&min_stat, &mechanisms); 321 | 322 | if (name != NULL) { 323 | display_name("Available cached credential %s\n", &name); 324 | (void) gss_release_name(&min_stat, &name); 325 | return KRB_CREDENTIAL_AVAILABLE; 326 | } 327 | return 0; 328 | } 329 | 330 | int acquire_credential(struct auth_s *credentials) { 331 | OM_uint32 min_stat, maj_stat; 332 | gss_name_t target_name; 333 | OM_uint32 lifetime = GSS_C_INDEFINITE; 334 | gss_cred_id_t *id; 335 | 336 | char *password = credentials->passnt; 337 | 338 | //!(g_creds->haskrb & KRB_CREDENTIAL_AVAILABLE) 339 | if (credentials->user && password) { 340 | char name[BUFSIZ]; 341 | strcpy(name, credentials->user); 342 | if (credentials->domain) { 343 | strcat(name, "@"); 344 | strcat(name, credentials->domain); 345 | } 346 | 347 | if ((maj_stat = acquire_name(&target_name, name, GSS_C_NT_USER_NAME)) 348 | != GSS_S_COMPLETE) 349 | return KRB_NO_CREDS; 350 | 351 | //TODO 352 | maj_stat = gss_acquire_cred(&min_stat, target_name, lifetime, 353 | GSS_C_NO_OID_SET, GSS_C_INITIATE, id, NULL, NULL); 354 | if (maj_stat != GSS_S_COMPLETE) { 355 | display_status("Acquire credential", maj_stat, min_stat); 356 | return KRB_NO_CREDS; 357 | } 358 | 359 | (void) gss_release_cred(&min_stat, id); 360 | 361 | (void) gss_release_name(&min_stat, &target_name); 362 | 363 | return KRB_CREDENTIAL_AVAILABLE; 364 | } 365 | return KRB_NO_CREDS; 366 | } 367 | -------------------------------------------------------------------------------- /direct.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 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | extern int h_errno; 35 | 36 | #include "utils.h" 37 | #include "globals.h" 38 | #include "auth.h" 39 | #include "http.h" 40 | #include "socket.h" 41 | #include "ntlm.h" 42 | #include "direct.h" 43 | #include "pages.h" 44 | 45 | int host_connect(const char *hostname, int port) { 46 | struct in_addr addr; 47 | 48 | errno = 0; 49 | if (!so_resolv(&addr, hostname)) { 50 | //if (debug) 51 | // printf("so_resolv: %s failed (%d: %s)\n", hostname, h_errno, hstrerror(h_errno)); 52 | return -1; 53 | } 54 | 55 | return so_connect(addr, port); 56 | 57 | } 58 | 59 | int www_authenticate(int sd, rr_data_t request, rr_data_t response, struct auth_s *creds) { 60 | char *tmp, *buf, *challenge; 61 | rr_data_t auth; 62 | int len; 63 | 64 | int rc = 0; 65 | 66 | buf = new(BUFSIZE); 67 | 68 | strcpy(buf, "NTLM "); 69 | len = ntlm_request(&tmp, creds); 70 | if (len) { 71 | to_base64(MEM(buf, uint8_t, 5), MEM(tmp, uint8_t, 0), len, BUFSIZE-5); 72 | free(tmp); 73 | } 74 | 75 | auth = dup_rr_data(request); 76 | auth->headers = hlist_mod(auth->headers, "Connection", "keep-alive", 1); 77 | auth->headers = hlist_mod(auth->headers, "Authorization", buf, 1); 78 | auth->headers = hlist_mod(auth->headers, "Content-Length", "0", 1); 79 | auth->headers = hlist_del(auth->headers, "Transfer-Encoding"); 80 | 81 | /* 82 | * Drop whatever error page server returned 83 | */ 84 | if (!http_body_drop(sd, response)) 85 | goto bailout; 86 | 87 | if (debug) { 88 | printf("\nSending WWW auth request...\n"); 89 | hlist_dump(auth->headers); 90 | } 91 | 92 | if (!headers_send(sd, auth)) 93 | goto bailout; 94 | 95 | if (debug) 96 | printf("\nReading WWW auth response...\n"); 97 | 98 | /* 99 | * Get NTLM challenge 100 | */ 101 | reset_rr_data(auth); 102 | if (!headers_recv(sd, auth)) { 103 | goto bailout; 104 | } 105 | 106 | if (debug) 107 | hlist_dump(auth->headers); 108 | 109 | /* 110 | * Auth required? 111 | */ 112 | if (auth->code == 401) { 113 | if (!http_body_drop(sd, auth)) 114 | goto bailout; 115 | 116 | tmp = hlist_get(auth->headers, "WWW-Authenticate"); 117 | if (tmp && strlen(tmp) > 6 + 8) { 118 | challenge = new(strlen(tmp) + 5 + 1); 119 | len = from_base64(challenge, tmp + 5); 120 | if (len > NTLM_CHALLENGE_MIN) { 121 | len = ntlm_response(&tmp, challenge, len, creds); 122 | if (len > 0) { 123 | strcpy(buf, "NTLM "); 124 | to_base64(MEM(buf, uint8_t, 5), MEM(tmp, uint8_t, 0), len, BUFSIZE-5); 125 | request->headers = hlist_mod(request->headers, "Authorization", buf, 1); 126 | free(tmp); 127 | } else { 128 | syslog(LOG_ERR, "No target info block. Cannot do NTLMv2!\n"); 129 | response->errmsg = "Invalid NTLM challenge from web server"; 130 | free(challenge); 131 | goto bailout; 132 | } 133 | } else { 134 | syslog(LOG_ERR, "Server returning invalid challenge!\n"); 135 | response->errmsg = "Invalid NTLM challenge from web server"; 136 | free(challenge); 137 | goto bailout; 138 | } 139 | 140 | free(challenge); 141 | } else { 142 | syslog(LOG_WARNING, "No challenge in WWW-Authenticate!\n"); 143 | response->errmsg = "Web server reply missing NTLM challenge"; 144 | goto bailout; 145 | } 146 | } else { 147 | goto bailout; 148 | } 149 | 150 | if (debug) 151 | printf("\nSending WWW auth...\n"); 152 | 153 | if (!headers_send(sd, request)) { 154 | goto bailout; 155 | } 156 | 157 | if (debug) 158 | printf("\nReading final server response...\n"); 159 | 160 | reset_rr_data(auth); 161 | if (!headers_recv(sd, auth)) { 162 | goto bailout; 163 | } 164 | 165 | rc = 1; 166 | 167 | if (debug) 168 | hlist_dump(auth->headers); 169 | 170 | bailout: 171 | if (rc) 172 | response = copy_rr_data(response, auth); 173 | free_rr_data(auth); 174 | free(buf); 175 | 176 | return rc; 177 | } 178 | 179 | rr_data_t direct_request(void *cdata, rr_data_t request) { 180 | rr_data_t data[2], rc = NULL; 181 | struct auth_s *tcreds = NULL; 182 | int *rsocket[2], *wsocket[2]; 183 | int w, loop, sd; 184 | char *tmp; 185 | 186 | char *hostname = NULL; 187 | int port = 0; 188 | int conn_alive = 0; 189 | 190 | int cd = ((struct thread_arg_s *)cdata)->fd; 191 | struct sockaddr_in caddr = ((struct thread_arg_s *)cdata)->addr; 192 | 193 | if (debug) 194 | printf("Direct thread processing...\n"); 195 | 196 | sd = host_connect(request->hostname, request->port); 197 | if (sd < 0) { 198 | syslog(LOG_WARNING, "Connection failed for %s:%d (%s)", request->hostname, request->port, strerror(errno)); 199 | tmp = gen_502_page(request->http, strerror(errno)); 200 | w = write(cd, tmp, strlen(tmp)); 201 | free(tmp); 202 | 203 | rc = (void *)-1; 204 | goto bailout; 205 | } 206 | 207 | /* 208 | * Now save NTLM credentials for purposes of this thread. 209 | * If web auth fails, we'll rewrite them like with NTLM-to-Basic in proxy mode. 210 | */ 211 | tcreds = dup_auth(g_creds, /* fullcopy */ 1); 212 | 213 | if (request->hostname) { 214 | hostname = strdup(request->hostname); 215 | port = request->port; 216 | } else { 217 | tmp = gen_502_page(request->http, "Invalid request URL"); 218 | w = write(cd, tmp, strlen(tmp)); 219 | free(tmp); 220 | 221 | rc = (void *)-1; 222 | goto bailout; 223 | } 224 | 225 | do { 226 | if (request) { 227 | data[0] = dup_rr_data(request); 228 | request = NULL; 229 | } else { 230 | data[0] = new_rr_data(); 231 | } 232 | data[1] = new_rr_data(); 233 | 234 | rsocket[0] = wsocket[1] = &cd; 235 | rsocket[1] = wsocket[0] = &sd; 236 | 237 | conn_alive = 0; 238 | 239 | for (loop = 0; loop < 2; ++loop) { 240 | if (data[loop]->empty) { // Isn't this the first loop with request supplied by caller? 241 | if (debug) { 242 | printf("\n******* Round %d C: %d, S: %d *******\n", loop+1, cd, sd); 243 | printf("Reading headers (%d)...\n", *rsocket[loop]); 244 | } 245 | if (!headers_recv(*rsocket[loop], data[loop])) { 246 | free_rr_data(data[0]); 247 | free_rr_data(data[1]); 248 | rc = (void *)-1; 249 | goto bailout; 250 | } 251 | } 252 | 253 | /* 254 | * Check whether this new request still talks to the same server as previous. 255 | * If no, return request to caller, he must decide on forward or direct 256 | * approach. 257 | */ 258 | if (loop == 0 && hostname && data[0]->hostname 259 | && (strcasecmp(hostname, data[0]->hostname) || port != data[0]->port)) { 260 | if (debug) 261 | printf("\n******* D RETURN: %s *******\n", data[0]->url); 262 | 263 | rc = dup_rr_data(data[0]); 264 | free_rr_data(data[0]); 265 | free_rr_data(data[1]); 266 | goto bailout; 267 | } 268 | 269 | if (debug) 270 | hlist_dump(data[loop]->headers); 271 | 272 | if (loop == 0 && data[0]->req) { 273 | syslog(LOG_DEBUG, "%s %s %s", inet_ntoa(caddr.sin_addr), data[0]->method, data[0]->url); 274 | 275 | /* 276 | * Convert full proxy request URL into a relative URL 277 | * Host header is already inserted by headers_recv() 278 | */ 279 | if (data[0]->rel_url) { 280 | if (data[0]->url) 281 | free(data[0]->url); 282 | data[0]->url = strdup(data[0]->rel_url); 283 | } 284 | 285 | data[0]->headers = hlist_mod(data[0]->headers, "Connection", "keep-alive", 1); 286 | data[0]->headers = hlist_del(data[0]->headers, "Proxy-Authorization"); 287 | 288 | /* 289 | * Try to get auth from client if present 290 | */ 291 | if (http_parse_basic(data[0]->headers, "Authorization", tcreds) > 0 && debug) 292 | printf("NTLM-to-basic: Credentials parsed: %s\\%s at %s\n", tcreds->domain, tcreds->user, tcreds->workstation); 293 | } 294 | 295 | /* 296 | * Is this a CONNECT request? 297 | */ 298 | if (loop == 0 && CONNECT(data[0])) { 299 | if (debug) 300 | printf("CONNECTing...\n"); 301 | 302 | data[1]->empty = 0; 303 | data[1]->req = 0; 304 | data[1]->code = 200; 305 | data[1]->msg = strdup("Connection established"); 306 | data[1]->http = strdup(data[0]->http); 307 | 308 | if (headers_send(cd, data[1])) 309 | tunnel(cd, sd); 310 | 311 | free_rr_data(data[0]); 312 | free_rr_data(data[1]); 313 | rc = (void *)-1; 314 | goto bailout; 315 | } 316 | 317 | if (loop == 1 && data[1]->code == 401 && hlist_subcmp_all(data[1]->headers, "WWW-Authenticate", "NTLM")) { 318 | /* 319 | * Server closing the connection after 401? 320 | * Should never happen. 321 | */ 322 | if (hlist_subcmp(data[1]->headers, "Connection", "close")) { 323 | if (debug) 324 | printf("Reconnect before WWW auth\n"); 325 | close(sd); 326 | sd = host_connect(data[0]->hostname, data[0]->port); 327 | if (sd < 0) { 328 | tmp = gen_502_page(data[0]->http, "WWW authentication reconnect failed"); 329 | w = write(cd, tmp, strlen(tmp)); 330 | free(tmp); 331 | 332 | rc = (void *)-1; 333 | goto bailout; 334 | } 335 | } 336 | if (!www_authenticate(*wsocket[0], data[0], data[1], tcreds)) { 337 | if (debug) 338 | printf("WWW auth connection error.\n"); 339 | 340 | tmp = gen_502_page(data[1]->http, data[1]->errmsg ? data[1]->errmsg : "Error during WWW-Authenticate"); 341 | w = write(cd, tmp, strlen(tmp)); 342 | free(tmp); 343 | 344 | free_rr_data(data[0]); 345 | free_rr_data(data[1]); 346 | 347 | rc = (void *)-1; 348 | goto bailout; 349 | } else if (data[1]->code == 401) { 350 | /* 351 | * Server giving 401 after auth? 352 | * Request basic auth 353 | */ 354 | tmp = gen_401_page(data[1]->http, data[0]->hostname, data[0]->port); 355 | w = write(cd, tmp, strlen(tmp)); 356 | free(tmp); 357 | 358 | free_rr_data(data[0]); 359 | free_rr_data(data[1]); 360 | 361 | rc = (void *)-1; 362 | goto bailout; 363 | } 364 | } 365 | 366 | /* 367 | * Check if we should loop for another request. Required for keep-alive 368 | * connections, client might really need a non-interrupted conversation. 369 | * 370 | * We default to keep-alive server connections, unless server explicitly 371 | * flags closing the connection or we detect a body with unknown size 372 | * (end marked by server closing). 373 | */ 374 | if (loop == 1) { 375 | conn_alive = !hlist_subcmp(data[1]->headers, "Connection", "close") 376 | && http_has_body(data[0], data[1]) != -1; 377 | if (conn_alive) { 378 | data[1]->headers = hlist_mod(data[1]->headers, "Proxy-Connection", "keep-alive", 1); 379 | data[1]->headers = hlist_mod(data[1]->headers, "Connection", "keep-alive", 1); 380 | } else { 381 | data[1]->headers = hlist_mod(data[1]->headers, "Proxy-Connection", "close", 1); 382 | rc = (void *)-1; 383 | } 384 | } 385 | 386 | if (debug) 387 | printf("Sending headers (%d)...\n", *wsocket[loop]); 388 | 389 | /* 390 | * Send headers 391 | */ 392 | if (!headers_send(*wsocket[loop], data[loop])) { 393 | free_rr_data(data[0]); 394 | free_rr_data(data[1]); 395 | rc = (void *)-1; 396 | goto bailout; 397 | } 398 | 399 | if (!http_body_send(*wsocket[loop], *rsocket[loop], data[0], data[1])) { 400 | free_rr_data(data[0]); 401 | free_rr_data(data[1]); 402 | rc = (void *)-1; 403 | goto bailout; 404 | } 405 | } 406 | 407 | free_rr_data(data[0]); 408 | free_rr_data(data[1]); 409 | 410 | } while (conn_alive && !so_closed(sd) && !so_closed(cd) && !serialize); 411 | 412 | bailout: 413 | if (tcreds) 414 | free(tcreds); 415 | if (hostname) 416 | free(hostname); 417 | 418 | close(sd); 419 | 420 | return rc; 421 | } 422 | 423 | void direct_tunnel(void *thread_data) { 424 | int sd, port = 0; 425 | char *pos, *hostname; 426 | 427 | int cd = ((struct thread_arg_s *)thread_data)->fd; 428 | char *thost = ((struct thread_arg_s *)thread_data)->target; 429 | struct sockaddr_in caddr = ((struct thread_arg_s *)thread_data)->addr; 430 | 431 | hostname = strdup(thost); 432 | if ((pos = strchr(hostname, ':')) != NULL) { 433 | *pos = 0; 434 | port = atoi(++pos); 435 | } 436 | 437 | sd = host_connect(hostname, port); 438 | if (sd <= 0) 439 | goto bailout; 440 | 441 | syslog(LOG_DEBUG, "%s FORWARD %s", inet_ntoa(caddr.sin_addr), thost); 442 | if (debug) 443 | printf("Portforwarding to %s for client %d...\n", thost, cd); 444 | 445 | tunnel(cd, sd); 446 | 447 | bailout: 448 | free(hostname); 449 | close(sd); 450 | close(cd); 451 | 452 | return; 453 | } 454 | 455 | -------------------------------------------------------------------------------- /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 | #include 26 | 27 | #include "ntlm.h" 28 | #include "swap.h" 29 | #include "xcrypt.h" 30 | #include "utils.h" 31 | #include "auth.h" 32 | 33 | extern int debug; 34 | 35 | static void ntlm_set_key(unsigned char *src, gl_des_ctx *context) { 36 | char key[8]; 37 | 38 | key[0] = src[0]; 39 | key[1] = ((src[0] << 7) & 0xff) | (src[1] >> 1); 40 | key[2] = ((src[1] << 6) & 0xff) | (src[2] >> 2); 41 | key[3] = ((src[2] << 5) & 0xff) | (src[3] >> 3); 42 | key[4] = ((src[3] << 4) & 0xff) | (src[4] >> 4); 43 | key[5] = ((src[4] << 3) & 0xff) | (src[5] >> 5); 44 | key[6] = ((src[5] << 2) & 0xff) | (src[6] >> 6); 45 | key[7] = (src[6] << 1) & 0xff; 46 | 47 | gl_des_setkey(context, key); 48 | } 49 | 50 | static int ntlm_calc_resp(char **dst, char *keys, char *challenge) { 51 | gl_des_ctx context; 52 | 53 | *dst = new(24 + 1); 54 | 55 | ntlm_set_key(MEM(keys, unsigned char, 0), &context); 56 | gl_des_ecb_encrypt(&context, challenge, *dst); 57 | 58 | ntlm_set_key(MEM(keys, unsigned char, 7), &context); 59 | gl_des_ecb_encrypt(&context, challenge, *dst+8); 60 | 61 | ntlm_set_key(MEM(keys, unsigned char, 14), &context); 62 | gl_des_ecb_encrypt(&context, challenge, *dst+16); 63 | 64 | return 24; 65 | } 66 | 67 | static void ntlm2_calc_resp(char **nthash, int *ntlen, char **lmhash, int *lmlen, 68 | char *passnt2, char *challenge, int tbofs, int tblen) { 69 | char *tmp, *blob, *nonce, *buf; 70 | int64_t tw; 71 | int blen; 72 | 73 | nonce = new(8 + 1); 74 | VAL(nonce, uint64_t, 0) = ((uint64_t)random() << 32) | random(); 75 | tw = ((uint64_t)time(NULL) + 11644473600LLU) * 10000000LLU; 76 | 77 | if (debug) { 78 | tmp = printmem(nonce, 8, 7); 79 | #ifdef PRId64 80 | printf("NTLMv2:\n\t Nonce: %s\n\tTimestamp: %"PRId64"\n", tmp, tw); 81 | #else 82 | printf("NTLMv2:\n\t Nonce: %s\n\tTimestamp: %ld\n", tmp, tw); 83 | #endif 84 | free(tmp); 85 | } 86 | 87 | blob = new(4+4+8+8+4+tblen+4 + 1); 88 | VAL(blob, uint32_t, 0) = U32LE(0x00000101); 89 | VAL(blob, uint32_t, 4) = U32LE(0); 90 | VAL(blob, uint64_t, 8) = U64LE(tw); 91 | VAL(blob, uint64_t, 16) = U64LE(VAL(nonce, uint64_t, 0)); 92 | VAL(blob, uint32_t, 24) = U32LE(0); 93 | memcpy(blob+28, MEM(challenge, char, tbofs), tblen); 94 | VAL(blob, uint32_t, 28+tblen) = U32LE(0); 95 | blen = 28+tblen+4; 96 | 97 | if (0 && debug) { 98 | tmp = printmem(blob, blen, 7); 99 | printf("\t Blob: %s (%d)\n", tmp, blen); 100 | free(tmp); 101 | } 102 | 103 | *ntlen = 16+blen; 104 | *nthash = new(*ntlen + 1); 105 | buf = new(8+blen + 1); 106 | memcpy(buf, MEM(challenge, char, 24), 8); 107 | memcpy(buf+8, blob, blen); 108 | hmac_md5(passnt2, 16, buf, 8+blen, *nthash); 109 | memcpy(*nthash+16, blob, blen); 110 | free(buf); 111 | 112 | *lmlen = 24; 113 | *lmhash = new(*lmlen + 1); 114 | buf = new(16 + 1); 115 | memcpy(buf, MEM(challenge, char, 24), 8); 116 | memcpy(buf+8, nonce, 8); 117 | hmac_md5(passnt2, 16, buf, 16, *lmhash); 118 | memcpy(*lmhash+16, nonce, 8); 119 | free(buf); 120 | 121 | free(blob); 122 | free(nonce); 123 | return; 124 | } 125 | 126 | static void ntlm2sr_calc_rest(char **nthash, int *ntlen, char **lmhash, int *lmlen, char *passnt, char *challenge) { 127 | char *sess, *nonce, *buf; 128 | 129 | nonce = new(8 + 1); 130 | VAL(nonce, uint64_t, 0) = ((uint64_t)random() << 32) | random(); 131 | 132 | *lmlen = 24; 133 | *lmhash = new(*lmlen + 1); 134 | memcpy(*lmhash, nonce, 8); 135 | memset(*lmhash+8, 0, 16); 136 | 137 | buf = new(16 + 1); 138 | sess = new(16 + 1); 139 | memcpy(buf, MEM(challenge, char, 24), 8); 140 | memcpy(buf+8, nonce, 8); 141 | md5_buffer(buf, 16, sess); 142 | free(buf); 143 | 144 | *ntlen = 24; 145 | ntlm_calc_resp(nthash, passnt, sess); 146 | 147 | free(sess); 148 | free(nonce); 149 | return; 150 | } 151 | 152 | char *ntlm_hash_lm_password(char *password) { 153 | char magic[8] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; 154 | gl_des_ctx context; 155 | char *keys, *pass; 156 | 157 | keys = new(21 + 1); 158 | pass = new(14 + 1); 159 | uppercase(strncpy(pass, password, MIN(14, strlen(password)))); 160 | 161 | ntlm_set_key(MEM(pass, unsigned char, 0), &context); 162 | gl_des_ecb_encrypt(&context, magic, keys); 163 | 164 | ntlm_set_key(MEM(pass, unsigned char, 7), &context); 165 | gl_des_ecb_encrypt(&context, magic, keys+8); 166 | 167 | memset(keys+16, 0, 5); 168 | memset(pass, 0, 14); 169 | free(pass); 170 | 171 | return keys; 172 | } 173 | 174 | char *ntlm_hash_nt_password(char *password) { 175 | char *u16, *keys; 176 | int len; 177 | 178 | keys = new(21 + 1); 179 | len = unicode(&u16, password); 180 | md4_buffer(u16, len, keys); 181 | 182 | memset(keys+16, 0, 5); 183 | memset(u16, 0, len); 184 | free(u16); 185 | 186 | return keys; 187 | } 188 | 189 | char *ntlm2_hash_password(char *username, char *domain, char *password) { 190 | char *tmp, *buf, *passnt, *passnt2; 191 | int len; 192 | 193 | passnt = ntlm_hash_nt_password(password); 194 | 195 | buf = new(strlen(username)+strlen(domain) + 1); 196 | strcat(buf, username); 197 | strcat(buf, domain); 198 | uppercase(buf); 199 | len = unicode(&tmp, buf); 200 | 201 | passnt2 = new(16 + 1); 202 | hmac_md5(passnt, 16, tmp, len, passnt2); 203 | 204 | free(passnt); 205 | free(tmp); 206 | free(buf); 207 | 208 | return passnt2; 209 | } 210 | 211 | int ntlm_request(char **dst, struct auth_s *creds) { 212 | char *buf, *tmp; 213 | int dlen, hlen; 214 | uint32_t flags = 0xb206; 215 | 216 | *dst = NULL; 217 | dlen = strlen(creds->domain); 218 | hlen = strlen(creds->workstation); 219 | 220 | if (!creds->flags) { 221 | if (creds->hashntlm2) 222 | flags = 0xa208b205; 223 | else if (creds->hashnt == 2) 224 | flags = 0xa208b207; 225 | else if (creds->hashnt && creds->hashlm) 226 | flags = 0xb207; 227 | else if (creds->hashnt) 228 | flags = 0xb205; 229 | else if (creds->hashlm) 230 | flags = 0xb206; 231 | else { 232 | if (debug) { 233 | printf("You're requesting with empty auth_s?!\n"); 234 | dump_auth(creds); 235 | } 236 | return 0; 237 | } 238 | } else 239 | flags = creds->flags; 240 | 241 | if (debug) { 242 | printf("NTLM Request:\n"); 243 | printf("\t Domain: %s\n", creds->domain); 244 | printf("\t Hostname: %s\n", creds->workstation); 245 | printf("\t Flags: 0x%X\n", (int)flags); 246 | } 247 | 248 | buf = new(NTLM_BUFSIZE); 249 | memcpy(buf, "NTLMSSP\0", 8); 250 | VAL(buf, uint32_t, 8) = U32LE(1); 251 | VAL(buf, uint32_t, 12) = U32LE(flags); 252 | VAL(buf, uint16_t, 16) = U16LE(dlen); 253 | VAL(buf, uint16_t, 18) = U16LE(dlen); 254 | VAL(buf, uint32_t, 20) = U32LE(32 + hlen); 255 | VAL(buf, uint16_t, 24) = U16LE(hlen); 256 | VAL(buf, uint16_t, 26) = U16LE(hlen); 257 | VAL(buf, uint32_t, 28) = U32LE(32); 258 | 259 | tmp = uppercase(strdup(creds->workstation)); 260 | memcpy(buf+32, tmp, hlen); 261 | free(tmp); 262 | 263 | tmp = uppercase(strdup(creds->domain)); 264 | memcpy(buf+32+hlen, tmp, dlen); 265 | free(tmp); 266 | 267 | *dst = buf; 268 | return 32+dlen+hlen; 269 | } 270 | 271 | static char *printuc(char *src, int len) { 272 | char *tmp; 273 | int i; 274 | 275 | tmp = new((len+1)/2 + 1); 276 | for (i = 0; i < len/2; ++i) { 277 | tmp[i] = src[i*2]; 278 | } 279 | 280 | return tmp; 281 | } 282 | 283 | /* 284 | void dump(char *src, int len) { 285 | int i, j; 286 | char *tmp; 287 | 288 | tmp = new(len*3+4); 289 | for (i = 0; i < len; ++i) { 290 | snprintf(tmp+i*3, 4, "%0hhX ", src[i]); 291 | printf("%c ", src[i]); 292 | } 293 | printf("\n%s\n", tmp); 294 | free(tmp); 295 | } 296 | */ 297 | 298 | int ntlm_response(char **dst, char *challenge, int challen, struct auth_s *creds) { 299 | char *buf, *udomain, *uuser, *uhost, *tmp; 300 | int dlen, ulen, hlen; 301 | uint16_t tpos, tlen, ttype = -1, tbofs = 0, tblen = 0; 302 | char *lmhash = NULL, *nthash = NULL; 303 | int lmlen = 0, ntlen = 0; 304 | 305 | if (debug) { 306 | printf("NTLM Challenge:\n"); 307 | tmp = printmem(MEM(challenge, char, 24), 8, 7); 308 | printf("\tChallenge: %s (len: %d)\n", tmp, challen); 309 | free(tmp); 310 | printf("\t Flags: 0x%X\n", U32LE(VAL(challenge, uint32_t, 20))); 311 | } 312 | 313 | if (challen > 48) { 314 | tbofs = tpos = U16LE(VAL(challenge, uint16_t, 44)); 315 | while (tpos+4 <= challen && (ttype = U16LE(VAL(challenge, uint16_t, tpos)))) { 316 | tlen = U16LE(VAL(challenge, uint16_t, tpos+2)); 317 | if (tpos+4+tlen > challen) 318 | break; 319 | 320 | if (debug) { 321 | switch (ttype) { 322 | case 0x1: 323 | printf("\t Server: "); 324 | break; 325 | case 0x2: 326 | printf("\tNT domain: "); 327 | break; 328 | case 0x3: 329 | printf("\t FQDN: "); 330 | break; 331 | case 0x4: 332 | printf("\t Domain: "); 333 | break; 334 | case 0x5: 335 | printf("\t TLD: "); 336 | break; 337 | default: 338 | printf("\t %3d: ", ttype); 339 | break; 340 | } 341 | tmp = printuc(MEM(challenge, char, tpos+4), tlen); 342 | printf("%s\n", tmp); 343 | free(tmp); 344 | } 345 | 346 | tpos += 4+tlen; 347 | tblen += 4+tlen; 348 | } 349 | 350 | if (tblen && ttype == 0) 351 | tblen += 4; 352 | 353 | if (debug) { 354 | printf("\t TBofs: %d\n\t TBlen: %d\n\t ttype: %d\n", tbofs, tblen, ttype); 355 | } 356 | } 357 | 358 | if (creds->hashntlm2 && !tblen) { 359 | return 0; 360 | } 361 | 362 | if (creds->hashntlm2) { 363 | ntlm2_calc_resp(&nthash, &ntlen, &lmhash, &lmlen, creds->passntlm2, challenge, tbofs, tblen); 364 | } 365 | 366 | if (creds->hashnt == 2) { 367 | ntlm2sr_calc_rest(&nthash, &ntlen, &lmhash, &lmlen, creds->passnt, challenge); 368 | } 369 | 370 | if (creds->hashnt == 1) { 371 | ntlen = ntlm_calc_resp(&nthash, creds->passnt, MEM(challenge, char, 24)); 372 | } 373 | 374 | if (creds->hashlm) { 375 | lmlen = ntlm_calc_resp(&lmhash, creds->passlm, MEM(challenge, char, 24)); 376 | } 377 | 378 | if (creds->hashnt || creds->hashntlm2) { 379 | tmp = uppercase(strdup(creds->domain)); 380 | dlen = unicode(&udomain, tmp); 381 | free(tmp); 382 | ulen = unicode(&uuser, creds->user); 383 | tmp = uppercase(strdup(creds->workstation)); 384 | hlen = unicode(&uhost, tmp); 385 | free(tmp); 386 | } else { 387 | udomain = uppercase(strdup(creds->domain)); 388 | uuser = uppercase(strdup(creds->user)); 389 | uhost = uppercase(strdup(creds->workstation)); 390 | 391 | dlen = strlen(creds->domain); 392 | ulen = strlen(creds->user); 393 | hlen = strlen(creds->workstation); 394 | } 395 | 396 | if (debug) { 397 | printf("NTLM Response:\n"); 398 | printf("\t Hostname: '%s'\n", creds->workstation); 399 | printf("\t Domain: '%s'\n", creds->domain); 400 | printf("\t Username: '%s'\n", creds->user); 401 | if (ntlen) { 402 | tmp = printmem(nthash, ntlen, 7); 403 | printf("\t Response: '%s' (%d)\n", tmp, ntlen); 404 | free(tmp); 405 | } 406 | if (lmlen) { 407 | tmp = printmem(lmhash, lmlen, 7); 408 | printf("\t Response: '%s' (%d)\n", tmp, lmlen); 409 | free(tmp); 410 | } 411 | } 412 | 413 | buf = new(NTLM_BUFSIZE); 414 | memcpy(buf, "NTLMSSP\0", 8); 415 | VAL(buf, uint32_t, 8) = U32LE(3); 416 | 417 | /* LM */ 418 | VAL(buf, uint16_t, 12) = U16LE(lmlen); 419 | VAL(buf, uint16_t, 14) = U16LE(lmlen); 420 | VAL(buf, uint32_t, 16) = U32LE(64+dlen+ulen+hlen); 421 | 422 | /* NT */ 423 | VAL(buf, uint16_t, 20) = U16LE(ntlen); 424 | VAL(buf, uint16_t, 22) = U16LE(ntlen); 425 | VAL(buf, uint32_t, 24) = U32LE(64+dlen+ulen+hlen+lmlen); 426 | 427 | /* Domain */ 428 | VAL(buf, uint16_t, 28) = U16LE(dlen); 429 | VAL(buf, uint16_t, 30) = U16LE(dlen); 430 | VAL(buf, uint32_t, 32) = U32LE(64); 431 | 432 | /* Username */ 433 | VAL(buf, uint16_t, 36) = U16LE(ulen); 434 | VAL(buf, uint16_t, 38) = U16LE(ulen); 435 | VAL(buf, uint32_t, 40) = U32LE(64+dlen); 436 | 437 | /* Hostname */ 438 | VAL(buf, uint16_t, 44) = U16LE(hlen); 439 | VAL(buf, uint16_t, 46) = U16LE(hlen); 440 | VAL(buf, uint32_t, 48) = U32LE(64+dlen+ulen); 441 | 442 | /* Session */ 443 | VAL(buf, uint16_t, 52) = U16LE(0); 444 | VAL(buf, uint16_t, 54) = U16LE(0); 445 | VAL(buf, uint16_t, 56) = U16LE(64+dlen+ulen+hlen+lmlen+ntlen); 446 | 447 | /* Flags */ 448 | VAL(buf, uint32_t, 60) = VAL(challenge, uint32_t, 20); 449 | 450 | memcpy(MEM(buf, char, 64), udomain, dlen); 451 | memcpy(MEM(buf, char, 64+dlen), uuser, ulen); 452 | memcpy(MEM(buf, char, 64+dlen+ulen), uhost, hlen); 453 | memcpy(MEM(buf, char, 64+dlen+ulen+hlen), lmhash, lmlen); 454 | memcpy(MEM(buf, char, 64+dlen+ulen+hlen+24), nthash, ntlen); 455 | 456 | if (nthash) 457 | free(nthash); 458 | if (lmhash) 459 | free(lmhash); 460 | 461 | free(uhost); 462 | free(uuser); 463 | free(udomain); 464 | 465 | *dst = buf; 466 | return 64+dlen+ulen+hlen+lmlen+ntlen; 467 | } 468 | -------------------------------------------------------------------------------- /http.c: -------------------------------------------------------------------------------- 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 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "utils.h" 34 | #include "socket.h" 35 | #include "ntlm.h" 36 | #include "http.h" 37 | 38 | #define BLOCK 2048 39 | 40 | extern int debug; 41 | 42 | /* 43 | * Ture if src is a header. This is just a basic check 44 | * for the colon delimiter. Might eventually become more 45 | * sophisticated. :) 46 | */ 47 | int is_http_header(const char *src) { 48 | return strcspn(src, ":") != strlen(src); 49 | } 50 | 51 | /* 52 | * Extract the header name from the source. 53 | */ 54 | char *get_http_header_name(const char *src) { 55 | int i; 56 | 57 | i = strcspn(src, ":"); 58 | if (i != strlen(src)) 59 | return substr(src, 0, i); 60 | else 61 | return NULL; 62 | } 63 | 64 | /* 65 | * Extract the header value from the source. 66 | */ 67 | char *get_http_header_value(const char *src) { 68 | char *sub; 69 | 70 | if ((sub = strchr(src, ':'))) { 71 | sub++; 72 | while (*sub == ' ') 73 | sub++; 74 | 75 | return strdup(sub); 76 | } else 77 | return NULL; 78 | } 79 | 80 | /* 81 | * Receive HTTP request/response from the given socket. Fill in pre-allocated 82 | * rr_data_t structure. 83 | * Returns: 1 if OK, 0 in case of socket EOF or other error 84 | */ 85 | int headers_recv(int fd, rr_data_t data) { 86 | int i, bsize; 87 | int len; 88 | char *buf; 89 | char *tok, *s3 = 0; 90 | char *orig = NULL; 91 | char *ccode = NULL; 92 | char *host = NULL; 93 | 94 | bsize = BUFSIZE; 95 | buf = new(bsize); 96 | 97 | i = so_recvln(fd, &buf, &bsize); 98 | if (i <= 0) 99 | goto bailout; 100 | 101 | if (debug) 102 | printf("HEAD: %s", buf); 103 | 104 | /* 105 | * Are we reading HTTP request (from client) or response (from server)? 106 | */ 107 | trimr(buf); 108 | orig = strdup(buf); 109 | len = strlen(buf); 110 | tok = strtok_r(buf, " ", &s3); 111 | if (tok && (!strncasecmp(buf, "HTTP/", 5) || !strncasecmp(tok, "ICY", 3))) { 112 | data->req = 0; 113 | data->empty = 0; 114 | data->http = strdup(tok); 115 | data->msg = NULL; 116 | 117 | tok = strtok_r(NULL, " ", &s3); 118 | if (tok) { 119 | ccode = strdup(tok); 120 | 121 | tok += strlen(ccode); 122 | while (tok < buf+len && *tok++ == ' '); 123 | 124 | if (strlen(tok)) 125 | data->msg = strdup(tok); 126 | } 127 | 128 | if (!data->msg) 129 | data->msg = strdup(""); 130 | 131 | if (!ccode || strlen(ccode) != 3 || (data->code = atoi(ccode)) == 0) { 132 | i = -2; 133 | goto bailout; 134 | } 135 | } else if (strstr(orig, " HTTP/") && tok) { 136 | data->req = 1; 137 | data->empty = 0; 138 | data->method = NULL; 139 | data->url = NULL; 140 | data->rel_url = NULL; 141 | data->http = NULL; 142 | data->hostname = NULL; 143 | 144 | data->method = strdup(tok); 145 | 146 | tok = strtok_r(NULL, " ", &s3); 147 | if (tok) 148 | data->url = strdup(tok); 149 | 150 | tok = strtok_r(NULL, " ", &s3); 151 | if (tok) 152 | data->http = strdup(tok); 153 | 154 | if (!data->url || !data->http) { 155 | i = -3; 156 | goto bailout; 157 | } 158 | 159 | if ((tok = strstr(data->url, "://"))) { 160 | tok += 3; 161 | } else { 162 | tok = data->url; 163 | } 164 | 165 | s3 = strchr(tok, '/'); 166 | if (s3) { 167 | host = substr(tok, 0, s3-tok); 168 | data->rel_url = strdup(s3); 169 | } else { 170 | host = substr(tok, 0, strlen(tok)); 171 | data->rel_url = strdup("/"); 172 | } 173 | 174 | } else { 175 | if (debug) 176 | printf("headers_recv: Unknown header (%s).\n", orig); 177 | i = -4; 178 | goto bailout; 179 | } 180 | 181 | /* 182 | * Read in all headers, do not touch any possible HTTP body 183 | */ 184 | do { 185 | i = so_recvln(fd, &buf, &bsize); 186 | trimr(buf); 187 | if (i > 0 && is_http_header(buf)) { 188 | data->headers = hlist_add(data->headers, get_http_header_name(buf), get_http_header_value(buf), HLIST_NOALLOC, HLIST_NOALLOC); 189 | } 190 | } while (strlen(buf) != 0 && i > 0); 191 | 192 | if (data->req) { 193 | /* 194 | * Fix requests, make sure the Host: header is present 195 | */ 196 | if (host && strlen(host)) { 197 | data->hostname = strdup(host); 198 | if (!hlist_get(data->headers, "Host")) 199 | data->headers = hlist_add(data->headers, "Host", host, HLIST_ALLOC, HLIST_ALLOC); 200 | } else { 201 | if (debug) 202 | printf("headers_recv: no host name (%s)\n", orig); 203 | i = -6; 204 | goto bailout; 205 | } 206 | 207 | /* 208 | * Remove port number from internal host name variable 209 | */ 210 | if (data->hostname && (tok = strchr(data->hostname, ':'))) { 211 | *tok = 0; 212 | data->port = atoi(tok+1); 213 | } else if (data->url) { 214 | if (!strncasecmp(data->url, "https", 5)) 215 | data->port = 443; 216 | else 217 | data->port = 80; 218 | } 219 | 220 | if (!strlen(data->hostname) || !data->port) { 221 | i = -5; 222 | goto bailout; 223 | } 224 | } 225 | 226 | bailout: 227 | if (orig) free(orig); 228 | if (ccode) free(ccode); 229 | if (host) free(host); 230 | free(buf); 231 | 232 | if (i <= 0) { 233 | if (debug) 234 | printf("headers_recv: fd %d error %d\n", fd, i); 235 | return 0; 236 | } 237 | 238 | return 1; 239 | } 240 | 241 | /* 242 | * Send HTTP request/response to the given socket based on what's in "data". 243 | * Returns: 1 if OK, 0 in case of socket error 244 | */ 245 | int headers_send(int fd, rr_data_t data) { 246 | hlist_t t; 247 | char *buf; 248 | int i, len; 249 | 250 | /* 251 | * First compute required buffer size (avoid realloc, etc) 252 | */ 253 | if (data->req) 254 | len = 20 + strlen(data->method) + strlen(data->url) + strlen(data->http); 255 | else 256 | len = 20 + strlen(data->http) + strlen(data->msg); 257 | 258 | t = data->headers; 259 | while (t) { 260 | len += 20 + strlen(t->key) + strlen(t->value); 261 | t = t->next; 262 | } 263 | 264 | /* 265 | * We know how much memory we need now... 266 | */ 267 | buf = new(len); 268 | 269 | /* 270 | * Prepare the first request/response line 271 | */ 272 | len = 0; 273 | if (data->req) 274 | len = sprintf(buf, "%s %s %s\r\n", data->method, data->url, data->http); 275 | else if (!data->skip_http) 276 | len = sprintf(buf, "%s %03d %s\r\n", data->http, data->code, data->msg); 277 | 278 | /* 279 | * Now add all headers. 280 | */ 281 | t = data->headers; 282 | while (t) { 283 | len += sprintf(buf+len, "%s: %s\r\n", t->key, t->value); 284 | t = t->next; 285 | } 286 | 287 | /* 288 | * Terminate headers 289 | */ 290 | strcat(buf, "\r\n"); 291 | 292 | /* 293 | * Flush it all down the toilet 294 | */ 295 | if (!so_closed(fd)) 296 | i = write(fd, buf, len+2); 297 | else 298 | i = -999; 299 | 300 | free(buf); 301 | 302 | if (i <= 0 || i != len+2) { 303 | if (debug) 304 | printf("headers_send: fd %d warning %d (connection closed)\n", fd, i); 305 | return 0; 306 | } 307 | 308 | return 1; 309 | } 310 | 311 | /* 312 | * Forward "size" of data from "src" to "dst". If size == -1 then keep 313 | * forwarding until src reaches EOF. 314 | * If dst == -1, data is discarded. 315 | */ 316 | int data_send(int dst, int src, length_t len) { 317 | char *buf; 318 | int i, block; 319 | int c = 0; 320 | int j = 1; 321 | 322 | if (!len) 323 | return 1; 324 | 325 | buf = new(BLOCK); 326 | 327 | do { 328 | block = (len == -1 || len-c > BLOCK ? BLOCK : len-c); 329 | i = read(src, buf, block); 330 | 331 | if (i > 0) 332 | c += i; 333 | 334 | if (dst >= 0 && debug) 335 | printf("data_send: read %d of %d / %d of %lld (errno = %s)\n", i, block, c, len, i < 0 ? strerror(errno) : "ok"); 336 | 337 | if (dst >= 0 && so_closed(dst)) { 338 | i = -999; 339 | break; 340 | } 341 | 342 | if (dst >= 0 && i > 0) { 343 | j = write(dst, buf, i); 344 | if (debug) 345 | printf("data_send: wrote %d of %d\n", j, i); 346 | } 347 | 348 | } while (i > 0 && j > 0 && (len == -1 || c < len)); 349 | 350 | free(buf); 351 | 352 | if (i <= 0 || j <= 0) { 353 | if (i == 0 && j > 0 && (len == -1 || c == len)) 354 | return 1; 355 | 356 | if (debug) 357 | printf("data_send: fds %d:%d warning %d (connection closed)\n", dst, src, i); 358 | return 0; 359 | } 360 | 361 | return 1; 362 | } 363 | 364 | /* 365 | * Forward chunked HTTP body from "src" descriptor to "dst". 366 | * If dst == -1, data is discarded. 367 | */ 368 | int chunked_data_send(int dst, int src) { 369 | char *buf; 370 | int bsize; 371 | int i, w, csize; 372 | 373 | char *err = NULL; 374 | 375 | bsize = BUFSIZE; 376 | buf = new(bsize); 377 | 378 | /* Take care of all chunks */ 379 | do { 380 | i = so_recvln(src, &buf, &bsize); 381 | if (i <= 0) { 382 | if (debug) 383 | printf("chunked_data_send: aborting, read error\n"); 384 | free(buf); 385 | return 0; 386 | } 387 | 388 | csize = strtol(buf, &err, 16); 389 | 390 | if (!isspace(*err) && *err != ';') { 391 | if (debug) 392 | printf("chunked_data_send: aborting, chunk size format error\n"); 393 | free(buf); 394 | return 0; 395 | } 396 | 397 | if (dst >= 0) 398 | i = write(dst, buf, strlen(buf)); 399 | 400 | if (csize) 401 | if (!data_send(dst, src, csize+2)) { 402 | if (debug) 403 | printf("chunked_data_send: aborting, data_send failed\n"); 404 | 405 | free(buf); 406 | return 0; 407 | } 408 | } while (csize != 0); 409 | 410 | /* Take care of possible trailer */ 411 | do { 412 | i = so_recvln(src, &buf, &bsize); 413 | if (dst >= 0 && i > 0) 414 | w = write(dst, buf, strlen(buf)); 415 | } while (i > 0 && buf[0] != '\r' && buf[0] != '\n'); 416 | 417 | free(buf); 418 | return 1; 419 | } 420 | 421 | /* 422 | * Full-duplex forwarding between proxy and client descriptors. 423 | * Used for bidirectional HTTP CONNECT connection. 424 | */ 425 | int tunnel(int cd, int sd) { 426 | fd_set set; 427 | int from, to, ret, sel; 428 | char *buf; 429 | 430 | buf = new(BUFSIZE); 431 | 432 | if (debug) 433 | printf("tunnel: select cli: %d, srv: %d\n", cd, sd); 434 | 435 | do { 436 | FD_ZERO(&set); 437 | FD_SET(cd, &set); 438 | FD_SET(sd, &set); 439 | 440 | sel = select(FD_SETSIZE, &set, NULL, NULL, NULL); 441 | if (sel > 0) { 442 | if (FD_ISSET(cd, &set)) { 443 | from = cd; 444 | to = sd; 445 | } else { 446 | from = sd; 447 | to = cd; 448 | } 449 | 450 | ret = read(from, buf, BUFSIZE); 451 | if (ret > 0) { 452 | ret = write(to, buf, ret); 453 | } else { 454 | free(buf); 455 | return (ret == 0); 456 | } 457 | } else if (sel < 0) { 458 | free(buf); 459 | return 0; 460 | } 461 | } while (1); 462 | 463 | free(buf); 464 | return 1; 465 | } 466 | 467 | /* 468 | * Return 0 if no body, -1 if body until EOF, number if size known 469 | * One of request/response can be NULL 470 | */ 471 | length_t http_has_body(rr_data_t request, rr_data_t response) { 472 | rr_data_t current; 473 | length_t length; 474 | int nobody; 475 | char *tmp; 476 | 477 | /* 478 | * Are we checking a complete req+res conversation or just the 479 | * request body? 480 | */ 481 | current = (!response || response->empty ? request : response); 482 | 483 | /* 484 | * HTTP body length decisions. There MUST NOT be any body from 485 | * server if the request was HEAD or reply is 1xx, 204 or 304. 486 | * No body can be in GET request if direction is from client. 487 | */ 488 | if (current == response) { 489 | nobody = (HEAD(request) || 490 | (response->code >= 100 && response->code < 200) || 491 | response->code == 204 || 492 | response->code == 304); 493 | } else { 494 | nobody = GET(request) || HEAD(request); 495 | } 496 | 497 | /* 498 | * Otherwise consult Content-Length. If present, we forward exaclty 499 | * that many bytes. 500 | * 501 | * If not present, but there is Transfer-Encoding or Content-Type 502 | * (or a request to close connection, that is, end of data is signaled 503 | * by remote close), we will forward until EOF. 504 | * 505 | * No C-L, no T-E, no C-T == no body. 506 | */ 507 | tmp = hlist_get(current->headers, "Content-Length"); 508 | if (!nobody && tmp == NULL && (hlist_in(current->headers, "Content-Type") 509 | || hlist_in(current->headers, "Transfer-Encoding") 510 | || hlist_subcmp(current->headers, "Connection", "close"))) { 511 | // || (response->code == 200) 512 | if (hlist_in(current->headers, "Transfer-Encoding") 513 | && hlist_subcmp(current->headers, "Transfer-Encoding", "chunked")) 514 | length = 1; 515 | else 516 | length = -1; 517 | } else 518 | length = (tmp == NULL || nobody ? 0 : atoll(tmp)); 519 | 520 | if (current == request && length == -1) 521 | length = 0; 522 | 523 | return length; 524 | } 525 | 526 | /* 527 | * Send a HTTP body (if any) between descriptors readfd and writefd 528 | */ 529 | int http_body_send(int writefd, int readfd, rr_data_t request, rr_data_t response) { 530 | length_t bodylen; 531 | int rc = 1; 532 | rr_data_t current; 533 | 534 | /* 535 | * Are we checking a complete req+res conversation or just the 536 | * request body? 537 | */ 538 | current = (response->empty ? request : response); 539 | 540 | /* 541 | * Ok, so do we expect any body? 542 | */ 543 | bodylen = http_has_body(request, response); 544 | if (bodylen) { 545 | /* 546 | * Check for supported T-E. 547 | */ 548 | if (hlist_subcmp(current->headers, "Transfer-Encoding", "chunked")) { 549 | if (debug) 550 | printf("Chunked body included.\n"); 551 | 552 | rc = chunked_data_send(writefd, readfd); 553 | if (debug) 554 | printf(rc ? "Chunked body sent.\n" : "Could not chunk send whole body\n"); 555 | } else { 556 | if (debug) 557 | printf("Body included. Length: %lld\n", bodylen); 558 | 559 | rc = data_send(writefd, readfd, bodylen); 560 | if (debug) 561 | printf(rc ? "Body sent.\n" : "Could not send whole body\n"); 562 | } 563 | } else if (debug) 564 | printf("No body.\n"); 565 | 566 | return rc; 567 | } 568 | 569 | /* 570 | * Connection cleanup - C-L or chunked body 571 | * Return 0 if connection closed or EOF, 1 if OK to continue 572 | */ 573 | int http_body_drop(int fd, rr_data_t response) { 574 | length_t bodylen; 575 | int rc = 1; 576 | 577 | bodylen = http_has_body(NULL, response); 578 | if (bodylen) { 579 | if (hlist_subcmp(response->headers, "Transfer-Encoding", "chunked")) { 580 | if (debug) 581 | printf("Discarding chunked body.\n"); 582 | rc = chunked_data_send(-1, fd); 583 | } else { 584 | if (debug) 585 | printf("Discarding %lld bytes.\n", bodylen); 586 | rc = data_send(-1, fd, bodylen); 587 | } 588 | } 589 | 590 | return rc; 591 | } 592 | 593 | /* 594 | * Parse headers for BASIC auth credentials 595 | * 596 | * Return 1 = creds parsed OK, 0 = no creds, -1 = invalid creds 597 | */ 598 | int http_parse_basic(hlist_t headers, const char *header, struct auth_s *tcreds) { 599 | char *tmp = NULL, *pos = NULL, *buf = NULL, *dom = NULL; 600 | int i; 601 | 602 | if (!hlist_subcmp(headers, header, "basic")) 603 | return 0; 604 | 605 | tmp = hlist_get(headers, header); 606 | buf = new(strlen(tmp) + 1); 607 | i = 5; 608 | while (i < strlen(tmp) && tmp[++i] == ' '); 609 | from_base64(buf, tmp+i); 610 | pos = strchr(buf, ':'); 611 | 612 | if (pos == NULL) { 613 | memset(buf, 0, strlen(buf)); /* clean password memory */ 614 | free(buf); 615 | return -1; 616 | } else { 617 | *pos = 0; 618 | dom = strchr(buf, '\\'); 619 | if (dom == NULL) { 620 | auth_strcpy(tcreds, user, buf); 621 | } else { 622 | *dom = 0; 623 | auth_strcpy(tcreds, domain, buf); 624 | auth_strcpy(tcreds, user, dom+1); 625 | } 626 | 627 | if (tcreds->hashntlm2) { 628 | tmp = ntlm2_hash_password(tcreds->user, tcreds->domain, pos+1); 629 | auth_memcpy(tcreds, passntlm2, tmp, 16); 630 | free(tmp); 631 | } 632 | 633 | if (tcreds->hashnt) { 634 | tmp = ntlm_hash_nt_password(pos+1); 635 | auth_memcpy(tcreds, passnt, tmp, 21); 636 | free(tmp); 637 | } 638 | 639 | if (tcreds->hashlm) { 640 | tmp = ntlm_hash_lm_password(pos+1); 641 | auth_memcpy(tcreds, passlm, tmp, 21); 642 | free(tmp); 643 | } 644 | 645 | memset(buf, 0, strlen(buf)); 646 | free(buf); 647 | } 648 | 649 | return 1; 650 | } 651 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 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 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "config/config.h" 34 | #include "swap.h" 35 | #include "utils.h" 36 | #include "socket.h" 37 | 38 | char hextab[17] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 0}; 39 | int hexindex[128] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 40 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 41 | -1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, 42 | -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 43 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1, 44 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; 45 | 46 | void myexit(int rc) { 47 | if (rc) 48 | fprintf(stderr, "Exitting with error. Check daemon logs or run with -v.\n"); 49 | 50 | exit(rc); 51 | } 52 | 53 | void croak(const char *msg, int console) { 54 | if (console) 55 | printf("%s", msg); 56 | else 57 | syslog(LOG_ERR, "%s", msg); 58 | 59 | myexit(1); 60 | } 61 | 62 | /* 63 | * Add a new item to a list. Every plist_t variable must be 64 | * initialized to NULL (or pass NULL for "list" when adding 65 | * the first item). This is for simplicity's sake (we don't 66 | * need any plist_new). 67 | * 68 | * This list type allows to store an arbitrary pointer 69 | * associating it with the key. 70 | */ 71 | plist_t plist_add(plist_t list, unsigned long key, void *aux) { 72 | plist_t tmp, t = list; 73 | 74 | tmp = malloc(sizeof(struct plist_s)); 75 | tmp->key = key; 76 | tmp->aux = aux; 77 | tmp->next = NULL; 78 | 79 | if (list == NULL) 80 | return tmp; 81 | 82 | while (t->next) 83 | t = t->next; 84 | 85 | t->next = tmp; 86 | 87 | return list; 88 | } 89 | 90 | /* 91 | * Delete an item from the list, possibly returning NULL when 92 | * the list is empty or nothing was found. 93 | */ 94 | plist_t plist_del(plist_t list, unsigned long key) { 95 | plist_t ot = NULL, t = list; 96 | 97 | while (t) { 98 | if (t->key == key) 99 | break; 100 | ot = t; 101 | t = t->next; 102 | } 103 | 104 | if (t) { 105 | plist_t tmp = t->next; 106 | 107 | if (t->aux) 108 | free(t->aux); 109 | free(t); 110 | if (ot == NULL) 111 | return tmp; 112 | 113 | ot->next = tmp; 114 | } 115 | 116 | return list; 117 | } 118 | 119 | /* 120 | * Return true if an item is present in the list. 121 | */ 122 | int plist_in(plist_t list, unsigned long key) { 123 | plist_t t = list; 124 | 125 | while (t) { 126 | if (t->key == key) 127 | break; 128 | t = t->next; 129 | } 130 | 131 | return (t != NULL); 132 | } 133 | 134 | /* 135 | * For debugging purposes - dump the entire contents 136 | * of a list. 137 | */ 138 | void plist_dump(plist_t list) { 139 | plist_t t; 140 | 141 | t = list; 142 | while (t) { 143 | printf("List data: %lu => 0x%8p\n", (unsigned long int)t->key, t->aux); 144 | t = t->next; 145 | } 146 | } 147 | 148 | /* 149 | * Return the pointer associated with the key. 150 | */ 151 | char *plist_get(plist_t list, int key) { 152 | plist_t t = list; 153 | 154 | while (t) { 155 | if (t->key == key) 156 | break; 157 | t = t->next; 158 | } 159 | 160 | return (t == NULL ? NULL : t->aux); 161 | } 162 | 163 | /* 164 | * Scan the list for an open descriptor (socket), possibly 165 | * discarding all closed ones on the way. Return the first 166 | * match. 167 | * 168 | * Use this method only for lists of descriptors! 169 | * 170 | * In conjunction with plist_add, the list behaves as a FIFO. 171 | * This feature is used for rotating cached connections in the 172 | * list, so that none is left too long unused (proxy timeout). 173 | * 174 | * Returns key value (descriptor) and if aux != NULL, *aux gets 175 | * aux pointer value (which caller must free if != NULL). 176 | */ 177 | 178 | int plist_pop(plist_t *list, void **aux) { 179 | plist_t tmp, t; 180 | int id = 0; 181 | int ok = 0; 182 | void *a = NULL; 183 | 184 | if (list == NULL || *list == NULL) 185 | return 0; 186 | 187 | t = *list; 188 | while (!ok && t) { 189 | id = t->key; 190 | a = t->aux; 191 | tmp = t->next; 192 | 193 | if (so_closed(id)) { 194 | close(id); 195 | if (t->aux) 196 | free(t->aux); 197 | } else 198 | ok = 1; 199 | 200 | free(t); 201 | t = tmp; 202 | } 203 | 204 | *list = t; 205 | 206 | if (ok) { 207 | if (aux != NULL) 208 | *aux = a; 209 | return id; 210 | } 211 | 212 | return 0; 213 | } 214 | 215 | /* 216 | * Return the number of items in a list. 217 | */ 218 | int plist_count(plist_t list) { 219 | plist_t t = list; 220 | int rc = 0; 221 | 222 | while (t) { 223 | rc++; 224 | t = t->next; 225 | } 226 | 227 | return rc; 228 | } 229 | 230 | /* 231 | * Free the list. 232 | */ 233 | plist_t plist_free(plist_t list) { 234 | plist_t t = list; 235 | 236 | while (list) { 237 | t = list->next; 238 | if (list->aux) 239 | free(list->aux); 240 | free(list); 241 | list = t; 242 | } 243 | 244 | return NULL; 245 | } 246 | 247 | /* 248 | * The same as plist_add. Here we have two other arguments. 249 | * They are boolean flags - HLIST_ALLOC means to duplicate a 250 | * key/value, HLIST_NOALLOC means to store the pointer directly. 251 | * 252 | * Caller decides this on a by-call basis. Part of the manipulation 253 | * routines is a "free". That method always deallocates both the 254 | * key and the value. So for static or temporary keys/values, 255 | * the caller can instruct us to duplicate the necessary amount 256 | * of heap. This mechanism is used to minimize memory-related 257 | * bugs throughout the code and tons of free's. 258 | */ 259 | hlist_t hlist_add(hlist_t list, char *key, char *value, hlist_add_t allockey, hlist_add_t allocvalue) { 260 | hlist_t tmp, t = list; 261 | 262 | if (key == NULL || value == NULL) 263 | return list; 264 | 265 | tmp = malloc(sizeof(struct hlist_s)); 266 | tmp->key = (allockey == HLIST_ALLOC ? strdup(key) : key); 267 | tmp->value = (allocvalue == HLIST_ALLOC ? strdup(value) : value); 268 | tmp->next = NULL; 269 | tmp->islist = 0; 270 | 271 | if (list == NULL) 272 | return tmp; 273 | 274 | while (t->next) 275 | t = t->next; 276 | 277 | t->next = tmp; 278 | 279 | return list; 280 | } 281 | 282 | /* 283 | * Return a duplicate of the list (copy). 284 | */ 285 | hlist_t hlist_dup(hlist_t list) { 286 | hlist_t tmp = NULL, t = list; 287 | 288 | while (t) { 289 | tmp = hlist_add(tmp, t->key, t->value, HLIST_ALLOC, HLIST_ALLOC); 290 | t = t->next; 291 | } 292 | 293 | return tmp; 294 | } 295 | 296 | /* 297 | * Remove an item from the list. 298 | */ 299 | hlist_t hlist_del(hlist_t list, const char *key) { 300 | hlist_t ot = NULL, t = list; 301 | 302 | while (t) { 303 | if (!strcasecmp(t->key, key)) 304 | break; 305 | ot = t; 306 | t = t->next; 307 | } 308 | 309 | if (t) { 310 | hlist_t tmp = t->next; 311 | 312 | free(t->key); 313 | free(t->value); 314 | free(t); 315 | 316 | if (ot == NULL) 317 | return tmp; 318 | 319 | ot->next = tmp; 320 | } 321 | 322 | return list; 323 | } 324 | 325 | /* 326 | * Change the value of a key. If add is true, we store it in the 327 | * list if the key is not found. Unlike hlist_add, which offers 328 | * pointer storage or memory duplication for both the key and the 329 | * value separately, hlist_mod always duplicates. 330 | * 331 | * Used to add a header, which might already be present. 332 | */ 333 | hlist_t hlist_mod(hlist_t list, char *key, char *value, int add) { 334 | hlist_t t = list; 335 | 336 | while (t) { 337 | if (!strcasecmp(t->key, key)) 338 | break; 339 | t = t->next; 340 | } 341 | 342 | if (t) { 343 | free(t->value); 344 | t->value = strdup(value); 345 | } else if (add) { 346 | list = hlist_add(list, key, value, HLIST_ALLOC, HLIST_ALLOC); 347 | } 348 | 349 | return list; 350 | } 351 | 352 | /* 353 | * Return true is the key is in the list. 354 | */ 355 | int hlist_in(hlist_t list, const char *key) { 356 | hlist_t t = list; 357 | 358 | while (t) { 359 | if (!strcasecmp(t->key, key)) 360 | break; 361 | t = t->next; 362 | } 363 | 364 | return (t != NULL); 365 | } 366 | 367 | /* 368 | * Return the number of items in a list. 369 | */ 370 | int hlist_count(hlist_t list) { 371 | hlist_t t = list; 372 | int rc = 0; 373 | 374 | while (t) { 375 | rc++; 376 | t = t->next; 377 | } 378 | 379 | return rc; 380 | } 381 | 382 | /* 383 | * Return the value for the key. 384 | */ 385 | char *hlist_get(hlist_t list, const char *key) { 386 | hlist_t t = list; 387 | 388 | while (t) { 389 | if (!strcasecmp(t->key, key)) 390 | break; 391 | t = t->next; 392 | } 393 | 394 | return (t == NULL ? NULL : t->value); 395 | } 396 | 397 | /* 398 | * Test if substr is part of the header's value. 399 | * Both case-insensitive. 400 | */ 401 | int hlist_subcmp(hlist_t list, const char *key, const char *substr) { 402 | int found = 0; 403 | char *tmp, *low; 404 | 405 | lowercase(low = strdup(substr)); 406 | tmp = hlist_get(list, key); 407 | if (tmp) { 408 | lowercase(tmp = strdup(tmp)); 409 | if (strstr(tmp, low)) 410 | found = 1; 411 | 412 | free(tmp); 413 | } 414 | 415 | free(low); 416 | return found; 417 | } 418 | 419 | /* 420 | * Test if substr is part of the header's value. 421 | * Both case-insensitive, checks all headers, not just first one. 422 | */ 423 | int hlist_subcmp_all(hlist_t list, const char *key, const char *substr) { 424 | hlist_t t = list; 425 | int found = 0; 426 | char *tmp, *low; 427 | 428 | lowercase(low = strdup(substr)); 429 | while (t) { 430 | if (!strcasecmp(t->key, key)) { 431 | lowercase(tmp = strdup(t->value)); 432 | if (strstr(tmp, low)) 433 | found = 1; 434 | 435 | free(tmp); 436 | } 437 | t = t->next; 438 | } 439 | 440 | free(low); 441 | return found; 442 | } 443 | 444 | /* 445 | * Free the list. For more about list memory management, 446 | * se hlist_add. 447 | */ 448 | hlist_t hlist_free(hlist_t list) { 449 | hlist_t t = list; 450 | 451 | while (list) { 452 | t = list->next; 453 | 454 | free(list->key); 455 | free(list->value); 456 | free(list); 457 | 458 | list = t; 459 | } 460 | 461 | return NULL; 462 | } 463 | 464 | /* 465 | * This is for debugging purposes. 466 | */ 467 | void hlist_dump(hlist_t list) { 468 | hlist_t t; 469 | 470 | t = list; 471 | while (t) { 472 | printf("%-30s => %s\n", t->key, t->value); 473 | t = t->next; 474 | } 475 | } 476 | 477 | /* 478 | * Standard substr. To prevent modification of the source 479 | * (terminating \x0), return the result in a new memory. 480 | */ 481 | char *substr(const char *src, int pos, int len) { 482 | int l; 483 | char *tmp; 484 | 485 | if (len == 0) 486 | len = strlen(src); 487 | 488 | l = MIN(len, strlen(src)-pos); 489 | if (l <= 0) 490 | return new(1); 491 | 492 | tmp = new(l+1); 493 | strlcpy(tmp, src+pos, l+1); 494 | 495 | return tmp; 496 | } 497 | 498 | /* 499 | * Allocate memory and initialize a new rr_data_t structure. 500 | */ 501 | rr_data_t new_rr_data(void) { 502 | rr_data_t data; 503 | 504 | data = malloc(sizeof(struct rr_data_s)); 505 | data->req = 0; 506 | data->code = 0; 507 | data->skip_http = 0; 508 | data->body_len = 0; 509 | data->empty = 1; 510 | data->port = 0; 511 | data->headers = NULL; 512 | data->method = NULL; 513 | data->url = NULL; 514 | data->rel_url = NULL; 515 | data->hostname = NULL; 516 | data->http = NULL; 517 | data->msg = NULL; 518 | data->body = NULL; 519 | data->errmsg = NULL; /* for static strings - we don't free, dup, nor copy */ 520 | 521 | return data; 522 | } 523 | 524 | /* 525 | * Copy the req/res data. 526 | */ 527 | rr_data_t copy_rr_data(rr_data_t dst, rr_data_t src) { 528 | if (src == NULL || dst == NULL) 529 | return NULL; 530 | 531 | reset_rr_data(dst); 532 | dst->req = src->req; 533 | dst->code = src->code; 534 | dst->skip_http = src->skip_http; 535 | dst->body_len = src->body_len; 536 | dst->empty = src->empty; 537 | dst->port = src->port; 538 | 539 | if (src->headers) 540 | dst->headers = hlist_dup(src->headers); 541 | if (src->method) 542 | dst->method = strdup(src->method); 543 | if (src->url) 544 | dst->url = strdup(src->url); 545 | if (src->rel_url) 546 | dst->rel_url = strdup(src->rel_url); 547 | if (src->hostname) 548 | dst->hostname = strdup(src->hostname); 549 | if (src->http) 550 | dst->http = strdup(src->http); 551 | if (src->msg) 552 | dst->msg = strdup(src->msg); 553 | if (src->body && src->body_len > 0) { 554 | dst->body = new(src->body_len); 555 | memcpy(dst->body, src->body, src->body_len); 556 | } 557 | 558 | return dst; 559 | } 560 | 561 | /* 562 | * Duplicate the req/res data. 563 | */ 564 | rr_data_t dup_rr_data(rr_data_t data) { 565 | rr_data_t tmp; 566 | 567 | if (data == NULL) 568 | return NULL; 569 | 570 | tmp = new_rr_data(); 571 | return copy_rr_data(tmp, data); 572 | } 573 | 574 | /* 575 | * Reset, freeing if neccessary 576 | */ 577 | rr_data_t reset_rr_data(rr_data_t data) { 578 | if (data == NULL) 579 | return NULL; 580 | 581 | data->req = 0; 582 | data->code = 0; 583 | data->skip_http = 0; 584 | data->body_len = 0; 585 | data->empty = 1; 586 | data->port = 0; 587 | 588 | if (data->headers) hlist_free(data->headers); 589 | if (data->method) free(data->method); 590 | if (data->url) free(data->url); 591 | if (data->rel_url) free(data->rel_url); 592 | if (data->hostname) free(data->hostname); 593 | if (data->http) free(data->http); 594 | if (data->msg) free(data->msg); 595 | if (data->body) free(data->body); 596 | 597 | data->headers = NULL; 598 | data->method = NULL; 599 | data->url = NULL; 600 | data->rel_url = NULL; 601 | data->hostname = NULL; 602 | data->http = NULL; 603 | data->msg = NULL; 604 | data->body = NULL; 605 | data->errmsg = NULL; 606 | 607 | return data; 608 | } 609 | 610 | /* 611 | * Free rr_data_t structure. We also take care of freeing 612 | * the memory of its members. 613 | */ 614 | void free_rr_data(rr_data_t data) { 615 | if (data == NULL) 616 | return; 617 | 618 | if (data->headers) hlist_free(data->headers); 619 | if (data->method) free(data->method); 620 | if (data->url) free(data->url); 621 | if (data->rel_url) free(data->rel_url); 622 | if (data->hostname) free(data->hostname); 623 | if (data->http) free(data->http); 624 | if (data->msg) free(data->msg); 625 | if (data->body) free(data->body); 626 | free(data); 627 | } 628 | 629 | /* 630 | * Cut the whitespace at the end of a string. 631 | */ 632 | char *trimr(char *buf) { 633 | int i; 634 | 635 | for (i = strlen(buf)-1; i >= 0 && isspace(buf[i]); --i); 636 | buf[i+1] = 0; 637 | 638 | return buf; 639 | } 640 | 641 | #if config_strdup == 0 642 | /* 643 | * Our implementation of non-POSIX strdup() 644 | */ 645 | char *strdup(const char *src) { 646 | size_t len; 647 | char *tmp; 648 | 649 | if (!src) 650 | return NULL; 651 | 652 | len = strlen(src)+1; 653 | tmp = calloc(1, len); 654 | memcpy(tmp, src, len-1); 655 | 656 | return tmp; 657 | } 658 | #endif 659 | 660 | /* 661 | * More intuitive version of strncpy with string termination 662 | * from OpenBSD 663 | */ 664 | size_t strlcpy(char *dst, const char *src, size_t siz) { 665 | char *d = dst; 666 | const char *s = src; 667 | size_t n = siz; 668 | 669 | /* Copy as many bytes as will fit */ 670 | if (n != 0) { 671 | while (--n != 0) { 672 | if ((*d++ = *s++) == '\0') 673 | break; 674 | } 675 | } 676 | 677 | /* Not enough room in dst, add NUL and traverse rest of src */ 678 | if (n == 0) { 679 | if (siz != 0) 680 | *d = '\0'; /* NUL-terminate dst */ 681 | while (*s++); 682 | } 683 | 684 | return (s - src - 1); /* count does not include NUL */ 685 | } 686 | 687 | /* 688 | * More intuitive version os strncat with string termination 689 | * from OpenBSD 690 | */ 691 | size_t strlcat(char *dst, const char *src, size_t siz) { 692 | char *d = dst; 693 | const char *s = src; 694 | size_t n = siz; 695 | size_t dlen; 696 | 697 | /* Find the end of dst and adjust bytes left but don't go past end */ 698 | while (n-- != 0 && *d != '\0') 699 | d++; 700 | 701 | dlen = d - dst; 702 | n = siz - dlen; 703 | 704 | if (n == 0) 705 | return(dlen + strlen(s)); 706 | 707 | while (*s != '\0') { 708 | if (n != 1) { 709 | *d++ = *s; 710 | n--; 711 | } 712 | s++; 713 | } 714 | *d = '\0'; 715 | 716 | return (dlen + (s - src)); /* count does not include NUL */ 717 | } 718 | 719 | /* 720 | * Shortcut for malloc/memset zero. 721 | */ 722 | char *new(size_t size) { 723 | char *tmp; 724 | 725 | tmp = malloc(size); 726 | memset(tmp, 0, size); 727 | 728 | return tmp; 729 | } 730 | 731 | /* 732 | * Self-explanatory. 733 | */ 734 | char *lowercase(char *str) { 735 | int i; 736 | 737 | for (i = 0; i < strlen(str); ++i) 738 | str[i] = tolower(str[i]); 739 | 740 | return str; 741 | } 742 | 743 | /* 744 | * Self-explanatory. 745 | */ 746 | char *uppercase(char *str) { 747 | int i; 748 | 749 | for (i = 0; i < strlen(str); ++i) 750 | str[i] = toupper(str[i]); 751 | 752 | return str; 753 | } 754 | 755 | int unicode(char **dst, char *src) { 756 | char *tmp; 757 | int l, i; 758 | 759 | if (!src) { 760 | *dst = NULL; 761 | return 0; 762 | } 763 | 764 | l = MIN(64, strlen(src)); 765 | tmp = new(2*l); 766 | for (i = 0; i < l; ++i) 767 | tmp[2*i] = src[i]; 768 | 769 | *dst = tmp; 770 | return 2*l; 771 | } 772 | 773 | char *urlencode(const char *str) { 774 | char *tmp; 775 | int i, pos; 776 | 777 | tmp = new(strlen(str)*3 + 1); 778 | for (pos = 0, i = 0; i < strlen(str); ++i) { 779 | if (isdigit(str[i]) || (tolower(str[i]) >= 'a' && tolower(str[i]) <= 'z') || str[i] == '.' || str[i] == '-' || str[i] == '_' || str[i] == '~') { 780 | tmp[pos++] = str[i]; 781 | } else { 782 | sprintf(tmp+pos, "%%%X", (unsigned char)str[i]); 783 | pos += 3; 784 | } 785 | } 786 | 787 | return tmp; 788 | } 789 | 790 | char *printmem(char *src, size_t len, int bitwidth) { 791 | char *tmp; 792 | int i; 793 | 794 | tmp = new(2*len+1); 795 | for (i = 0; i < len; ++i) { 796 | tmp[i*2] = hextab[((uint8_t)src[i] ^ (uint8_t)(7-bitwidth)) >> 4]; 797 | tmp[i*2+1] = hextab[(src[i] ^ (uint8_t)(7-bitwidth)) & 0x0F]; 798 | } 799 | 800 | return tmp; 801 | } 802 | 803 | char *scanmem(char *src, int bitwidth) { 804 | int h, l, i, bytes; 805 | char *tmp; 806 | 807 | if (strlen(src) % 2) 808 | return NULL; 809 | 810 | bytes = strlen(src)/2; 811 | tmp = new(bytes+1); 812 | for (i = 0; i < bytes; ++i) { 813 | h = hexindex[(int)src[i*2]]; 814 | l = hexindex[(int)src[i*2+1]]; 815 | if (h < 0 || l < 0) { 816 | free(tmp); 817 | return NULL; 818 | } 819 | tmp[i] = ((h << 4) + l) ^ (uint8_t)(7-bitwidth); 820 | } 821 | tmp[i] = 0; 822 | 823 | return tmp; 824 | } 825 | 826 | 827 | 828 | /* 829 | * BASE64 CODE FROM MUTT BEGIN - ORIGINAL COPYRIGHT APPLIES: 830 | * 831 | * Copyright (C) 1996-2001 Michael R. Elkins 832 | * Copyright (C) 1996-2001 Brandon Long 833 | * Copyright (C) 1997-2001 Thomas Roessler 834 | * Copyright (C) 1998-2001 Werner Koch 835 | * Copyright (C) 1999-2001 Brendan Cully 836 | * Copyright (C) 1999-2001 Tommi Komulainen 837 | * Copyright (C) 2000-2001 Edmund Grimley Evans 838 | * 839 | */ 840 | 841 | #define BAD -1 842 | #define base64val(c) index64[(unsigned int)(c)] 843 | 844 | char base64[64] = { 845 | 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', 846 | 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', 847 | 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', 848 | 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', 849 | '4','5','6','7','8','9','+','/' 850 | }; 851 | 852 | int index64[128] = { 853 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 854 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 855 | -1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60, 856 | 61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13, 857 | 14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26, 858 | 27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45, 859 | 46,47,48,49,50,51,-1,-1,-1,-1,-1 860 | }; 861 | 862 | void to_base64(unsigned char *out, const unsigned char *in, size_t len, size_t olen) { 863 | while (len >= 3 && olen > 10) { 864 | *out++ = base64[in[0] >> 2]; 865 | *out++ = base64[((in[0] << 4) & 0x30) | (in[1] >> 4)]; 866 | *out++ = base64[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; 867 | *out++ = base64[in[2] & 0x3f]; 868 | olen -= 4; 869 | len -= 3; 870 | in += 3; 871 | } 872 | 873 | /* clean up remainder */ 874 | if (len > 0 && olen > 4) { 875 | unsigned char fragment; 876 | 877 | *out++ = base64[in[0] >> 2]; 878 | fragment = (in[0] << 4) & 0x30; 879 | if (len > 1) 880 | fragment |= in[1] >> 4; 881 | *out++ = base64[fragment]; 882 | *out++ = (len < 2) ? '=' : base64[(in[1] << 2) & 0x3c]; 883 | *out++ = '='; 884 | } 885 | *out = '\0'; 886 | } 887 | 888 | /* Convert '\0'-terminated base 64 string to raw bytes. 889 | * Returns length of returned buffer, or -1 on error */ 890 | int from_base64(char *out, const char *in) 891 | { 892 | int len = 0; 893 | register unsigned char digit1, digit2, digit3, digit4; 894 | 895 | do { 896 | digit1 = in[0]; 897 | if (digit1 > 127 || base64val (digit1) == BAD) 898 | return -1; 899 | 900 | digit2 = in[1]; 901 | if (digit2 > 127 || base64val (digit2) == BAD) 902 | return -1; 903 | 904 | digit3 = in[2]; 905 | if (digit3 > 127 || ((digit3 != '=') && (base64val (digit3) == BAD))) 906 | return -1; 907 | 908 | digit4 = in[3]; 909 | if (digit4 > 127 || ((digit4 != '=') && (base64val (digit4) == BAD))) 910 | return -1; 911 | 912 | in += 4; 913 | 914 | /* digits are already sanity-checked */ 915 | *out++ = (base64val(digit1) << 2) | (base64val(digit2) >> 4); 916 | len++; 917 | if (digit3 != '=') { 918 | *out++ = ((base64val(digit2) << 4) & 0xf0) | (base64val(digit3) >> 2); 919 | len++; 920 | if (digit4 != '=') { 921 | *out++ = ((base64val(digit3) << 6) & 0xc0) | base64val(digit4); 922 | len++; 923 | } 924 | } 925 | } while (*in && digit4 != '='); 926 | 927 | return len; 928 | } 929 | /* 930 | * CODE FROM MUTT END 931 | */ 932 | --------------------------------------------------------------------------------