├── .gitignore ├── main.py ├── runserver.bat ├── doc ├── greenball.gif ├── BUGS ├── research.txt ├── INSTALL.txt ├── README.txt ├── changelog.txt └── ENCRYPTION.txt ├── packaging ├── compile.py ├── readme ├── setup.py ├── ntlmaps.spec └── ntlmaps.nsi ├── scripts ├── ntlmaps-hashes └── ntlmaps ├── ntlmaps ├── __init__.py ├── logger.py ├── ntlm_procs.py ├── des.py ├── U32.py ├── config.py ├── basic_auth.py ├── digest_messages.py ├── digest_auth.py ├── utils.py ├── monitor_upstream.py ├── config_affairs.py ├── server.py ├── md4.py ├── des_c.py ├── http_header.py ├── ntlm_messages.py ├── ntlm_auth.py └── des_data.py ├── Makefile ├── init └── ntlmaps ├── server.cfg └── COPYING /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | link scripts/ntlmaps -------------------------------------------------------------------------------- /runserver.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | "c:\program files\python\python.exe" scripts/ntlmaps 3 | -------------------------------------------------------------------------------- /doc/greenball.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohannesBuchner/digestaps/master/doc/greenball.gif -------------------------------------------------------------------------------- /packaging/compile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | compile.py 5 | Copyright (C) 2004 Darryl Dixon 6 | This program may be freely redistributed under the terms of the GNU GPL 7 | """ 8 | 9 | from compileall import compile_dir 10 | import sys 11 | 12 | compile_dir(sys.argv[1]) 13 | -------------------------------------------------------------------------------- /scripts/ntlmaps-hashes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os, sys, base64 4 | # to allow running from source distribution 5 | sys.path = list((os.getcwd(),)) + sys.path 6 | from ntlmaps import ntlm_procs 7 | 8 | if len(sys.argv) < 2: 9 | print "usage: ntlmaps-hashes yourpassword" 10 | sys.exit(1) 11 | 12 | print "Set LM_HASHED_PW to " + base64.encodestring(ntlm_procs.create_LM_hashed_password(sys.argv[1:][0])) 13 | print "Set NT_HASHED_PW to " + base64.encodestring(ntlm_procs.create_NT_hashed_password(sys.argv[1:][0])) 14 | -------------------------------------------------------------------------------- /doc/BUGS: -------------------------------------------------------------------------------- 1 | Digest authentication seems to work with squid-3.1.8. 2 | Doesn't seem to work well with squid 3.0.STABLE13. 3 | More testing and beta-testers needed! 4 | 5 | 6 | There is an issue with APS working as a standalone proxy. It serves requests from 7 | an http-client one by one and allows persistent connections, then it may receive several 8 | request in very sort time to one thread, and one of them may be to almost dead banner 9 | site, then all the requests made after that one will be waiting till that "bad" connection 10 | will be closed due to timeout. So I suggest switching off HTTP/1.1 presistent connections 11 | in your browser when you are using APS for web (not proxy) authentication and surfing 12 | banner rich evironment. 13 | -------------------------------------------------------------------------------- /packaging/readme: -------------------------------------------------------------------------------- 1 | The files in this directory are included for the convenience of all packagers 2 | of ntlmaps on any platform. Please feel free to contribute if you can see 3 | enhancements or improvements to be made. Directory contents so far: 4 | ntlmaps.spec -- Distribution-neutral RPM .spec file. 5 | 6 | compile.py -- Basic wrapper of compileall.compile_dir() for convenience 7 | of RPM builders. 8 | setup.py -- Script for building standalone ntlmaps distributables with 9 | py2exe on Windows (execute out of base directory as: 10 | python packaging\setup.py). 11 | ntlmaps.nsi -- NSIS (Nullsoft Scriptable Installer) configuration for 12 | turning py2exe'ed ntlmaps into a pretty installer for 13 | Windows. 14 | -------------------------------------------------------------------------------- /doc/research.txt: -------------------------------------------------------------------------------- 1 | Since release 0.9.7 APS has options related to authentication process. 2 | 3 | For now there are AUTH_DEBUG and NTLM_FLAGS available. 4 | 5 | AUTH_DEBUG:1 makes APS write detailed report on NTLM authentication dialog into *.auth 6 | files in APS' working directory. Actually not very useful but it may be helpful for 7 | understanding the NTLM authentication itself. 8 | 9 | NTLM_FLAGS: some_hex_digits forces APS to use custom NTLM flags during the authentication. 10 | For now it is known very little on their effects. So this option may be of use for 11 | better understanding of NTLM authentication method. 12 | 13 | NOTE: that flag format in NTLM_FLAGS is somewhat different from that in *.auth files 14 | reported with AUTH_DEBUG:1. 15 | 16 | NOTE 2: Looks like all this stuff will be useless without access to MS web or proxy server. -------------------------------------------------------------------------------- /ntlmaps/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright 2004 Darryl A. Dixon 2 | # and is part of 'NTLM Authorization Proxy Server', 3 | # Copyright 2001 Dmitry A. Rozmanov 4 | # 5 | # NTLM APS is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # NTLM APS is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with the sofware; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 18 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 19 | # 20 | from ntlmaps import config, config_affairs, server 21 | all = ('config', 'config_affairs', 'server') 22 | -------------------------------------------------------------------------------- /doc/INSTALL.txt: -------------------------------------------------------------------------------- 1 | INSTALLATION 2 | ------------ 3 | 4 | 1. Edit server.cfg to match your preferences 5 | 6 | 2. Run the ntlmaps script. 7 | 8 | 9 | Windows: 10 | --------- 11 | runserver.bat 12 | 13 | Unix: 14 | --------- 15 | /path/to/scripts/ntlmaps & 16 | 17 | or 18 | 19 | python /path/to/scripts/ntlmaps 20 | 21 | 3. Try it! Go to your browser and set 127.0.0.1 at port 5865 as 22 | your HTTP/FTP proxy server. 5865 is the default port, if you have not 23 | changed it in server.cfg, change this appropriatelly. 24 | 25 | --------------------------------------------------------------------------------------- 26 | Tip: 27 | --------------------------------------------------------------------------------------- 28 | There is a command line option "-c config_file_name" for pointing APS to config 29 | files other than the default server.cfg in the working directory. 30 | 31 | --------------------------------------------------------------------------------------- 32 | If server fails to authenticate you at MS Proxy with NTLM, you may want to try 33 | changing FULL_NTLM in server.cfg to 1. 34 | 35 | Such message in debug file suggest this problem: 36 | 37 | *** Authentication methods allowed: NTLM 38 | *** Looks like our authorization failed. 39 | *** Passing 407(401) to client. 40 | --------------------------------------------------------------------------------------- 41 | 42 | -------------------------------------------------------------------------------- /scripts/ntlmaps: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | 3 | # This file is part of 'NTLM Authorization Proxy Server' 4 | # Copyright 2001 Dmitry A. Rozmanov 5 | # 6 | # NTLM APS is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # NTLM APS is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with the sofware; see the file COPYING. If not, write to the 18 | # Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 20 | # 21 | import sys, os 22 | 23 | # to allow running from source distribution 24 | sys.path = list((os.getcwd(),)) + sys.path 25 | import ntlmaps 26 | 27 | #-------------------------------------------------------------- 28 | # config affairs 29 | # look for default config name in ntlmaps/config.py 30 | conf = ntlmaps.config.read_config(ntlmaps.config.findConfigFileNameInArgv(sys.argv)) 31 | 32 | conf['GENERAL']['VERSION'] = '1.0' 33 | 34 | #-------------------------------------------------------------- 35 | print 'NTLM authorization Proxy Server v%s' % conf['GENERAL']['VERSION'] 36 | print 'Copyright (C) 2001-2009 by Dmitry Rozmanov, Darryl Dixon, and others.' 37 | 38 | config = ntlmaps.config_affairs.arrange(conf) 39 | 40 | 41 | #-------------------------------------------------------------- 42 | # let's run it 43 | serv = ntlmaps.server.AuthProxyServer(config) 44 | serv.run() 45 | -------------------------------------------------------------------------------- /ntlmaps/logger.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import time 21 | 22 | #----------------------------------------------------------------------- 23 | class Logger: 24 | "provides facility for loggin messages during runtime" 25 | 26 | #----------------------------------------------------------------------- 27 | def __init__(self, log_name, debug_level = 1): 28 | "logger init routine" 29 | 30 | self.log_name = log_name 31 | self.debug_level = debug_level 32 | 33 | #----------------------------------------------------------------------- 34 | def log(self, str): 35 | "writes string to log file" 36 | 37 | if self.debug_level: 38 | 39 | tstr = '' 40 | # tstr = '(' + time.strftime('%H:%M:%S', time.localtime(time.time())) + ') ' 41 | # time.clock() 42 | 43 | fptr = open(self.log_name, 'a') 44 | fptr.write(tstr + str) 45 | fptr.close() 46 | -------------------------------------------------------------------------------- /packaging/setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | 3 | # This file is Copyright 2005 Mario Zoppetti, and was added by 4 | # Darryl A. Dixon to 5 | # 'NTLM Authorization Proxy Server', 6 | # Copyright 2001 Dmitry A. Rozmanov 7 | # 8 | # NTLM APS is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # NTLM APS is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with the sofware; see the file COPYING. If not, write to the 20 | # Free Software Foundation, Inc., 21 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 22 | # 23 | # setup.py 24 | from distutils.core import setup 25 | import sys, re, string, os 26 | 27 | try: 28 | import py2exe 29 | sys.argv.append("py2exe") 30 | use_py2exe=True 31 | except: 32 | use_py2exe=False 33 | 34 | 35 | if use_py2exe: 36 | serverCfgDir='' 37 | else: 38 | serverCfgDir='/etc/ntlmaps' 39 | 40 | setup(name='ntlmaps', 41 | version='1.0', 42 | description='NTLM Authorization Proxy Server', 43 | url='http://ntlmaps.sourceforge.net/', 44 | packages=['ntlmaps'], 45 | scripts=['scripts/ntlmaps', 'scripts/ntlmaps-hashes'], 46 | data_files=[(serverCfgDir, ['server.cfg']), 47 | ('/etc/rc.d/init.d', ['init/ntlmaps'])], 48 | options = {"py2exe": {"packages": ["encodings", "win32console"], 49 | "optimize": 2}}, 50 | ) 51 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RELEASE_DATE := "23-Feb-2009" 2 | RELEASE_MAJOR := 1 3 | RELEASE_MINOR := 0 4 | RELEASE_EXTRALEVEL := 5 | RELEASE_NAME := ntlmaps 6 | RELEASE_VERSION := $(RELEASE_MAJOR).$(RELEASE_MINOR)$(RELEASE_EXTRALEVEL) 7 | RELEASE_STRING := $(RELEASE_NAME)-$(RELEASE_VERSION) 8 | 9 | SPEC=packaging/ntlmaps.spec 10 | TARBALL=dist/$(RELEASE_STRING).tar.bz2 11 | ZIP=dist/$(RELEASE_STRING).zip 12 | .PHONY = all tarball 13 | 14 | all: 15 | 16 | clean: 17 | -rm -rf *.rpm *~ dist/ build/ 18 | 19 | tarball: $(TARBALL) 20 | zip: $(ZIP) 21 | 22 | releasedir: 23 | cp -ar ../trunk $${tmp_dir}/$(RELEASE_STRING) ; \ 24 | find $${tmp_dir}/$(RELEASE_STRING) -depth -name .svn -type d -exec rm -rf \{\} \; ; \ 25 | find $${tmp_dir}/$(RELEASE_STRING) -depth -name lib -type d -exec rm -rf \{\} \; ; \ 26 | find $${tmp_dir}/$(RELEASE_STRING) -depth -name dist -type d -exec rm -rf \{\} \; ; \ 27 | find $${tmp_dir}/$(RELEASE_STRING) -depth -name build -type d -exec rm -rf \{\} \; ; \ 28 | find $${tmp_dir}/$(RELEASE_STRING) -depth -name \*~ -type f -exec rm -f \{\} \; ; \ 29 | find $${tmp_dir}/$(RELEASE_STRING) -depth -name \*.rpm -type f -exec rm -f \{\} \; ; \ 30 | sync ; sync ; sync ; 31 | 32 | $(TARBALL): 33 | sync ; sync ; sync 34 | mkdir -p dist 35 | tmp_dir=`mktemp -d /tmp/ntlmaps.XXXXXXXX` ; \ 36 | make releasedir tmp_dir=$${tmp_dir} ; \ 37 | tar cvjf $(TARBALL) -C $${tmp_dir} $(RELEASE_STRING) ; \ 38 | rm -rf $${tmp_dir} ; 39 | 40 | $(ZIP): 41 | sync ; sync ; sync 42 | mkdir -p dist 43 | oldcwd=`pwd` ; \ 44 | tmp_dir=`mktemp -d /tmp/ntlmaps.XXXXXXXX` ; \ 45 | make releasedir tmp_dir=$${tmp_dir} ; \ 46 | pushd $${tmp_dir} ; \ 47 | zip -r $${oldcwd}/$(ZIP) $(RELEASE_STRING) ; \ 48 | popd ; \ 49 | rm -rf $${tmp_dir} ; 50 | 51 | rpm: tarball $(SPEC) 52 | tmp_dir=`mktemp -d /tmp/ntlmaps.XXXXXXXX` ; \ 53 | mkdir -p $${tmp_dir}/{BUILD,RPMS,SRPMS,SPECS,SOURCES} ; \ 54 | cp $(TARBALL) $${tmp_dir}/SOURCES ; \ 55 | cp $(SPEC) $${tmp_dir}/SPECS ; \ 56 | pushd $${tmp_dir} > /dev/null 2>&1; \ 57 | rpmbuild -ba --define "_topdir $${tmp_dir}" SPECS/ntlmaps.spec ; \ 58 | popd > /dev/null 2>&1; \ 59 | cp $${tmp_dir}/RPMS/noarch/* $${tmp_dir}/SRPMS/* . ; \ 60 | rm -rf $${tmp_dir} ; \ 61 | rpmlint *.rpm 62 | 63 | sign: $(TARBALL) 64 | gpg --armor --detach-sign $(TARBALL) 65 | mv "$(TARBALL).asc" "`dirname $(TARBALL)`/`basename $(TARBALL) .asc`.sign" 66 | -------------------------------------------------------------------------------- /init/ntlmaps: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # ntlmaps - NTLM Authentication Proxy 4 | # 5 | # chkconfig: - 98 10 6 | # description: Proxy software that allows you \ 7 | # to authenticate via a Microsoft Proxy Server \ 8 | # using the proprietary NTLM protocol. 9 | # 10 | ### BEGIN INIT INFO 11 | # Provides: ntlmaps 12 | # Required-Start: $local_fs $network $named 13 | # Required-Stop: $local_fs $network 14 | # Default-Stop: 15 | # Short-Description: start and stop ntlmaps daemon 16 | # Description: Proxy software that allows you to authenticate via a 17 | # Microsoft Proxy Server using the proprietary NTLM 18 | # protocol. 19 | ### END INIT INFO 20 | 21 | # Source function library. 22 | . /etc/rc.d/init.d/functions 23 | 24 | exec="/usr/bin/ntlmaps" 25 | prog="ntlmaps" 26 | lockfile=/var/lock/subsys/ntlmaps 27 | 28 | start() { 29 | [ -x $exec ] || exit 5 30 | echo -n $"Starting $prog: " 31 | daemon $exec > /dev/null 2>&1 & 32 | retval=$? 33 | echo 34 | [ $retval -eq 0 ] && touch $lockfile 35 | return $retval 36 | } 37 | 38 | stop() { 39 | echo -n $"Stopping $prog: " 40 | killproc ${prog} 41 | retval=$? 42 | echo 43 | [ $retval -eq 0 ] && rm -f $lockfile 44 | return $retval 45 | } 46 | 47 | restart() { 48 | stop 49 | start 50 | } 51 | 52 | reload() { 53 | restart 54 | } 55 | 56 | force_reload() { 57 | restart 58 | } 59 | 60 | rh_status() { 61 | # run checks to determine if the service is running or use generic status 62 | status $prog 63 | } 64 | 65 | rh_status_q() { 66 | rh_status >/dev/null 2>&1 67 | } 68 | 69 | 70 | case "$1" in 71 | start) 72 | rh_status_q && exit 0 73 | $1 74 | ;; 75 | stop) 76 | rh_status_q || exit 0 77 | $1 78 | ;; 79 | restart) 80 | $1 81 | ;; 82 | reload) 83 | rh_status_q || exit 7 84 | $1 85 | ;; 86 | force-reload) 87 | force_reload 88 | ;; 89 | status) 90 | rh_status 91 | ;; 92 | condrestart|try-restart) 93 | rh_status_q || exit 0 94 | restart 95 | ;; 96 | *) 97 | echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" 98 | exit 2 99 | esac 100 | exit $? 101 | -------------------------------------------------------------------------------- /packaging/ntlmaps.spec: -------------------------------------------------------------------------------- 1 | # ntlmaps.spec 2 | # Copyright (C) 2004 Darryl Dixon 3 | # This program may be freely redistributed under the terms of the GNU GPLv2+ 4 | %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} 5 | 6 | Name: ntlmaps 7 | Version: 1.0 8 | Release: 1%{?dist} 9 | Summary: NTLM Authorization Proxy Server 10 | 11 | Group: Applications/Internet 12 | License: GPLv2+ 13 | URL: http://ntlmaps.sourceforge.net 14 | Source0: http://downloads.sourceforge.net/ntlmaps/%{name}-%{version}.tar.bz2 15 | BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) 16 | BuildRequires: python-devel >= 1.5.2, dos2unix 17 | BuildArch: noarch 18 | Requires(post): chkconfig 19 | Requires(preun): chkconfig 20 | # This is for /sbin/service 21 | Requires(preun): initscripts 22 | 23 | %description 24 | NTLM Authorization Proxy Server is a proxy software that allows you to 25 | authenticate via a Microsoft Proxy Server using the proprietary NTLM 26 | protocol. Since version 0.9.5 APS has an ability to behave as a 27 | standalone proxy server and authenticate http clients at web servers 28 | using NTLM method. 29 | 30 | %prep 31 | %setup -q 32 | 33 | %build 34 | %{__python} packaging/setup.py build 35 | 36 | %install 37 | rm -rf $RPM_BUILD_ROOT 38 | %{__python} packaging/setup.py install -O1 --skip-build --root $RPM_BUILD_ROOT 39 | dos2unix COPYING doc/* 40 | 41 | %clean 42 | rm -rf $RPM_BUILD_ROOT 43 | 44 | %files 45 | %defattr(-,root,root) 46 | %doc COPYING doc/* 47 | %{python_sitelib}/* 48 | %dir %{_sysconfdir}/%{name} 49 | %config(noreplace) %{_sysconfdir}/%{name}/server.cfg 50 | %{_bindir}/%{name}* 51 | %{_sysconfdir}/rc.d/init.d/%{name} 52 | 53 | %post 54 | /sbin/chkconfig --add %{name} 55 | 56 | %preun 57 | if [ $1 = 0 ] ; then 58 | /sbin/service %{name} stop >/dev/null 2>&1 59 | /sbin/chkconfig --del %{name} 60 | fi 61 | 62 | 63 | %changelog 64 | * Mon Apr 13 2009 Matt Domsch - 1.0-1 65 | - minor cleanups. 66 | - finally a 1.0 release! 67 | 68 | * Tue Oct 21 2008 Matt Domsch - 0.9.9.8-1 69 | - cleanup for Fedora packaging 70 | 71 | * Tue Jul 05 2005 Darryl Dixon 72 | [ntlmaps-0.9.9.6] 73 | - Mark server.cfg as config file 74 | 75 | * Fri Jun 10 2005 Darryl Dixon 76 | [ntlmaps-0.9.9.4] 77 | - Move server.cfg to %%{_sysconfdir} for better FHS compliance 78 | 79 | * Thu Feb 24 2005 Darryl Dixon 80 | [ntlmaps-0.9.9.3] 81 | - Update for moved file locations in source dir 82 | - Use %%{ntlmaps_dir} 83 | - Move server.cfg to %%{_localstatedir}%{ntlmaps_dir} (/var/opt/ntlmaps) 84 | 85 | * Wed Feb 23 2005 Darryl Dixon 86 | [ntlmaps-0.9.9.2] 87 | - Initial release of .spec file 88 | -------------------------------------------------------------------------------- /ntlmaps/ntlm_procs.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import string 21 | import des, md4, utils 22 | 23 | #--------------------------------------------------------------------- 24 | #takes a 21 byte array and treats it as 3 56-bit DES keys. The 25 | #8 byte plaintext is encrypted with each key and the resulting 24 26 | #bytes are stored in the result array 27 | 28 | def calc_resp(keys_str, plain_text): 29 | "keys_str - hashed password" 30 | "plain_text - nonce from server" 31 | res = '' 32 | dobj = des.DES(keys_str[0:7]) 33 | res = res + dobj.encrypt(plain_text[0:8]) 34 | 35 | dobj = des.DES(keys_str[7:14]) 36 | res = res + dobj.encrypt(plain_text[0:8]) 37 | 38 | dobj = des.DES(keys_str[14:21]) 39 | res = res + dobj.encrypt(plain_text[0:8]) 40 | 41 | return res 42 | 43 | #--------------------------------------------------------------------- 44 | def create_LM_hashed_password(passwd): 45 | "setup LanManager password" 46 | "create LanManager hashed password" 47 | 48 | lm_pw = '\000' * 14 49 | 50 | passwd = string.upper(passwd) 51 | 52 | if len(passwd) < 14: 53 | lm_pw = passwd + lm_pw[len(passwd) - 14:] 54 | else: lm_pw = passwd[0:14] 55 | 56 | # do hash 57 | 58 | magic_lst = [0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25] 59 | magic_str = utils.lst2str(magic_lst) 60 | 61 | res = '' 62 | dobj = des.DES(lm_pw[0:7]) 63 | res = res + dobj.encrypt(magic_str) 64 | 65 | dobj = des.DES(lm_pw[7:14]) 66 | res = res + dobj.encrypt(magic_str) 67 | 68 | # addig zeros to get 21 bytes string 69 | res = res + '\000\000\000\000\000' 70 | 71 | return res 72 | #--------------------------------------------------------------------- 73 | def create_NT_hashed_password(passwd): 74 | "create NT hashed password" 75 | 76 | # we have to have UNICODE password 77 | pw = utils.str2unicode(passwd) 78 | 79 | # do MD4 hash 80 | md4_context = md4.new() 81 | md4_context.update(pw) 82 | 83 | res = md4_context.digest() 84 | 85 | # addig zeros to get 21 bytes string 86 | res = res + '\000\000\000\000\000' 87 | 88 | return res 89 | 90 | -------------------------------------------------------------------------------- /ntlmaps/des.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import des_c, utils 21 | 22 | #--------------------------------------------------------------------- 23 | class DES: 24 | 25 | des_c_obj = None 26 | 27 | #----------------------------------------------------------------- 28 | def __init__(self, key_str): 29 | "" 30 | k = str_to_key56(key_str) 31 | k = key56_to_key64(k) 32 | key_str = utils.lst2str(k) 33 | self.des_c_obj = des_c.DES(key_str) 34 | 35 | #----------------------------------------------------------------- 36 | def encrypt(self, plain_text): 37 | "" 38 | return self.des_c_obj.encrypt(plain_text) 39 | 40 | #----------------------------------------------------------------- 41 | def decrypt(self, crypted_text): 42 | "" 43 | return self.des_c_obj.decrypt(crypted_text) 44 | 45 | #--------------------------------------------------------------------- 46 | #Some Helpers 47 | #--------------------------------------------------------------------- 48 | 49 | DESException = 'DESException' 50 | 51 | #--------------------------------------------------------------------- 52 | def str_to_key56(key_str): 53 | "" 54 | if type(key_str) != type(''): 55 | #rise DESException, 'ERROR. Wrong key type.' 56 | pass 57 | if len(key_str) < 7: 58 | key_str = key_str + '\000\000\000\000\000\000\000'[:(7 - len(key_str))] 59 | key_56 = [] 60 | for i in key_str[:7]: key_56.append(ord(i)) 61 | 62 | return key_56 63 | 64 | #--------------------------------------------------------------------- 65 | def key56_to_key64(key_56): 66 | "" 67 | key = [] 68 | for i in range(8): key.append(0) 69 | 70 | key[0] = key_56[0]; 71 | key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); 72 | key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); 73 | key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); 74 | key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); 75 | key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); 76 | key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); 77 | key[7] = (key_56[6] << 1) & 0xFF; 78 | 79 | key = set_key_odd_parity(key) 80 | 81 | return key 82 | 83 | #--------------------------------------------------------------------- 84 | def set_key_odd_parity(key): 85 | "" 86 | for i in range(len(key)): 87 | for k in range(7): 88 | bit = 0 89 | t = key[i] >> k 90 | bit = (t ^ bit) & 0x1 91 | key[i] = (key[i] & 0xFE) | bit 92 | 93 | return key 94 | -------------------------------------------------------------------------------- /ntlmaps/U32.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | 21 | C = 0x1000000000L 22 | 23 | def norm(n): 24 | return n & 0xFFFFFFFFL 25 | 26 | 27 | class U32: 28 | v = 0L 29 | 30 | def __init__(self, value = 0): 31 | self.v = C + norm(abs(long(value))) 32 | 33 | def set(self, value = 0): 34 | self.v = C + norm(abs(long(value))) 35 | 36 | def __repr__(self): 37 | return hex(norm(self.v)) 38 | 39 | def __long__(self): return long(norm(self.v)) 40 | def __int__(self): return int(norm(self.v)) 41 | def __chr__(self): return chr(norm(self.v)) 42 | 43 | def __add__(self, b): 44 | r = U32() 45 | r.v = C + norm(self.v + b.v) 46 | return r 47 | 48 | def __sub__(self, b): 49 | r = U32() 50 | if self.v < b.v: 51 | r.v = C + norm(0x100000000L - (b.v - self.v)) 52 | else: r.v = C + norm(self.v - b.v) 53 | return r 54 | 55 | def __mul__(self, b): 56 | r = U32() 57 | r.v = C + norm(self.v * b.v) 58 | return r 59 | 60 | def __div__(self, b): 61 | r = U32() 62 | r.v = C + (norm(self.v) / norm(b.v)) 63 | return r 64 | 65 | def __mod__(self, b): 66 | r = U32() 67 | r.v = C + (norm(self.v) % norm(b.v)) 68 | return r 69 | 70 | def __neg__(self): return U32(self.v) 71 | def __pos__(self): return U32(self.v) 72 | def __abs__(self): return U32(self.v) 73 | 74 | def __invert__(self): 75 | r = U32() 76 | r.v = C + norm(~self.v) 77 | return r 78 | 79 | def __lshift__(self, b): 80 | r = U32() 81 | r.v = C + norm(self.v << b) 82 | return r 83 | 84 | def __rshift__(self, b): 85 | r = U32() 86 | r.v = C + (norm(self.v) >> b) 87 | return r 88 | 89 | def __and__(self, b): 90 | r = U32() 91 | r.v = C + norm(self.v & b.v) 92 | return r 93 | 94 | def __or__(self, b): 95 | r = U32() 96 | r.v = C + norm(self.v | b.v) 97 | return r 98 | 99 | def __xor__(self, b): 100 | r = U32() 101 | r.v = C + norm(self.v ^ b.v) 102 | return r 103 | 104 | def __not__(self): 105 | return U32(not norm(self.v)) 106 | 107 | def truth(self): 108 | return norm(self.v) 109 | 110 | def __cmp__(self, b): 111 | if norm(self.v) > norm(b.v): return 1 112 | elif norm(self.v) < norm(b.v): return -1 113 | else: return 0 114 | 115 | def __nonzero__(self): 116 | return norm(self.v) -------------------------------------------------------------------------------- /ntlmaps/config.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import string, getopt, os, sys 21 | 22 | #------------------------------------------------------------------------------------------- 23 | def read_config(fname): 24 | res = {} 25 | 26 | buf = open(fname).readlines() 27 | for line in buf: 28 | workingLine = string.strip(line) 29 | if workingLine: 30 | if workingLine[0] != '#': 31 | if workingLine[0] == '[' and workingLine[-1] == ']': 32 | section_name = string.strip(workingLine[1:-1]) 33 | if section_name: 34 | res[section_name] = {} 35 | else: 36 | parts = string.split(workingLine, ':') 37 | if len(parts) > 1: 38 | res[section_name][string.strip(parts[0])] = string.strip(string.join(parts[1:], ':')) 39 | return res 40 | 41 | #------------------------------------------------------------------------------------------- 42 | # Thanks Janek Schwarz for this addition. 43 | 44 | def findConfigFileNameInArgv(argv): 45 | """ Resolves configuration file. Resolution goes as follows: 46 | if the command switch '-c' is given its argument is taken as 47 | the config file. Otherwise the function falls back to 48 | the value of the NTLMAPS_CONF environment variable, 49 | 'server.cfg', in the current directory, 50 | '$HOME/.ntlmaps.conf', 51 | and finally /etc/ntlmaps/server.cfg, in order. """ 52 | 53 | try: 54 | home=os.path.join(os.getenv('HOME'), '.ntlmaps.conf') 55 | except: 56 | home=None 57 | 58 | possible_paths = ( 59 | os.getenv('NTLMAPS_CONF'), 60 | os.path.join(os.getcwd(), 'server.cfg'), 61 | home, 62 | '/etc/ntlmaps/server.cfg' 63 | ) 64 | 65 | 66 | configFileName = None 67 | 68 | optionsList, notUsedArguments = getopt.getopt(argv[1:], 'c:') 69 | 70 | for i in optionsList: 71 | option, value = i 72 | if option == '-c' and value != '': 73 | try: 74 | handle = open(value) 75 | handle.close() 76 | configFileName = value 77 | break 78 | except IOError: 79 | print "ERROR: Config file specified with '-c' either does not exist or is not readable." 80 | sys.exit(1) 81 | 82 | if configFileName is None: 83 | for p in possible_paths: 84 | if p is not None: 85 | if os.path.exists(p): 86 | configFileName = p 87 | break 88 | 89 | if configFileName is None: 90 | sys.stderr.write('Unable to find a config file.\n') 91 | sys.exit(1) 92 | print "Using config file %s" % configFileName 93 | 94 | 95 | return configFileName 96 | -------------------------------------------------------------------------------- /ntlmaps/basic_auth.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | # a few bug fixes by T. Sai Deep - 28/03/2007 21 | 22 | import base64 23 | 24 | class basic_auther: 25 | 26 | #----------------------------------------------------------------------- 27 | def __init__(self): 28 | "" 29 | pass 30 | 31 | #----------------------------------------------------------------------- 32 | def build_credentials(self, config_dic): 33 | "" 34 | 35 | msg = config_dic['USER'] + ":" + config_dic['PASSWORD'] 36 | msg = base64.encodestring(msg) 37 | msg = msg.replace('\012', '') 38 | 39 | return msg 40 | 41 | #----------------------------------------------------------------------- 42 | def proxy_basic_authentication(self, connection): 43 | "" 44 | connection.logger.log('*** Basic authorization in progress...\n') 45 | 46 | connection.close_rserver() 47 | connection.connect_rserver() 48 | 49 | connection.reset_rserver() 50 | connection.rserver_buffer = '' 51 | connection.logger.log('*** Remote server buffer flushed.\n') 52 | 53 | basic_string = self.build_credentials(connection.config['GENERAL']) 54 | 55 | tmp_client_head_obj = connection.client_head_obj.copy() 56 | tmp_client_head_obj.replace_param_value('Proxy-Authorization', 'Basic ' + basic_string) 57 | 58 | connection.logger.log('*** Sending client header (not body) with Basic auth...') 59 | tmp_client_head_obj.send(connection.rserver_socket) 60 | connection.logger.log('Done.\n') 61 | connection.logger.log('*** New client header with Basic auth:\n=====\n' + tmp_client_head_obj.__repr__()) 62 | 63 | # upon exit all the remote server variables are reset 64 | # so new remote server response will be taken by the usual way in connection.run() 65 | connection.logger.log('*** End of Basic authorization process.\n') 66 | 67 | #----------------------------------------------------------------------- 68 | def www_basic_authentication(self, connection): 69 | "" 70 | connection.logger.log('*** Basic authorization in progress...\n') 71 | 72 | connection.close_rserver() 73 | connection.connect_rserver() 74 | 75 | connection.reset_rserver() 76 | connection.rserver_buffer = '' 77 | connection.logger.log('*** Remote server buffer flushed.\n') 78 | 79 | basic_string = self.build_credentials(connection.config['GENERAL']) 80 | 81 | tmp_client_head_obj = connection.client_head_obj.copy() 82 | tmp_client_head_obj.replace_param_value('Authorization', 'Basic ' + basic_sting) 83 | 84 | connection.logger.log('*** Sending client header (not body) with Basic auth...') 85 | tmp_client_head_obj.send(connection.rserver_socket) 86 | connection.logger.log('Done.\n') 87 | connection.logger.log('*** New client header with Basic auth:\n=====\n' + tmp_client_head_obj.__repr__()) 88 | 89 | # upon exit all the remote server variables are reset 90 | # so new remote server response will be taken by the usual way in connection.run() 91 | connection.logger.log('*** End of Basic authorization process.\n') 92 | 93 | -------------------------------------------------------------------------------- /ntlmaps/digest_messages.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import ntlm_procs, utils 21 | import base64, string 22 | import hashlib 23 | import time 24 | 25 | from urllib2 import randombytes 26 | 27 | class digest_messages: 28 | nonce_count = 0 29 | 30 | def get_algorithm_impls(self, algorithm): 31 | # lambdas assume digest modules are imported at the top level 32 | if algorithm == 'MD5': 33 | H = lambda x: hashlib.md5(x).hexdigest() 34 | elif algorithm == 'SHA1': 35 | H = lambda x: hashlib.sha1(x).hexdigest() 36 | # MD5-sess not implemented 37 | KD = lambda s, d: H("%s:%s" % (s, d)) 38 | return H, KD 39 | 40 | def debug_message2(self, msg): 41 | return 'MESSAGE2:=> ' + str(self.parse_message2(msg)) + "\n" 42 | 43 | def parse_message2(self, msg): 44 | d = {} 45 | for pair in msg.split(","): 46 | split = pair.split("=") 47 | k = split[0].strip(' ') 48 | if len(split) == 2: 49 | v = split[1].strip(' ,"') 50 | d[k] = v 51 | else: 52 | pass 53 | #v = k 54 | return d 55 | 56 | def get_cnonce(self, nonce): 57 | dig = hashlib.sha1("%s:%s:%s:%s" % (self.nonce_count, nonce, 58 | time.ctime(), randombytes(8))).hexdigest() 59 | return dig[:16] 60 | 61 | def create_response3(self, chal, env, req = None): 62 | username = env['USER'] 63 | password = env['PASSWORD'] 64 | realm = chal['realm'] 65 | nonce = chal['nonce'] 66 | qop = chal.get('qop') 67 | algorithm = chal.get('algorithm', 'MD5') 68 | opaque = chal.get('opaque') 69 | 70 | H, KD = self.get_algorithm_impls(algorithm) 71 | entitydigest = None 72 | 73 | if False and algorithm and algorithm == 'MD5-sess': 74 | A1 = H('%s:%s:%s:%s:%s', (username, realm, password)) + ("%s:%s" % (nonce,cnonce)) 75 | else: 76 | A1 = '%s:%s:%s' % (username, realm, password) 77 | 78 | if req is None: 79 | method = 'GET' 80 | digesturi = '/' 81 | else: 82 | method = req.get_http_method() 83 | digesturi = req.get_http_url() 84 | 85 | if not qop or qop == 'auth' or True: 86 | A2 = method + ':' + digesturi 87 | elif qop == 'auth-int': 88 | A2 = method + ':' + H(body) 89 | 90 | if qop and qop == 'auth': 91 | self.nonce_count += 1 92 | ncvalue = '%08x' % self.nonce_count 93 | cnonce = self.get_cnonce(nonce) 94 | reqdigest = KD(H(A1), nonce+":"+ncvalue+":"+cnonce+":"+qop+":"+H(A2)) 95 | else: 96 | reqdigest = KD(H(A1), nonce+":"+H(A2)) 97 | 98 | response = chal 99 | response['response'] = reqdigest 100 | if opaque: 101 | response['opaque'] = opaque 102 | else: 103 | response['digest'] = entitydigest 104 | response['nc'] = ncvalue 105 | response['opaque'] = opaque 106 | response['cnonce'] = cnonce 107 | response['uri'] = digesturi 108 | response['username'] = username 109 | del response['stale'] 110 | return response 111 | 112 | def create_message3(self, chal, env, req): 113 | response = self.create_response3(chal, env, req) 114 | pairs = [] 115 | for k in response.keys(): 116 | v = response[k] 117 | if v is not None: 118 | pairs.append('%s="%s"' % (k, v)) 119 | text = ", ".join(pairs) 120 | return text 121 | 122 | def debug_message3(self, msg): 123 | return msg 124 | 125 | -------------------------------------------------------------------------------- /ntlmaps/digest_auth.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import string, select, base64 21 | import digest_messages, utils 22 | 23 | digest_messages = digest_messages.digest_messages() 24 | 25 | # 26 | # See RFC2617 27 | # Also used bits from urllib2.AbstractDigestAuthHandler 28 | # 29 | 30 | class digest_auther: 31 | """ 32 | Digest authenticator class. Makes an HTTP authentication using Digest method. 33 | """ 34 | lastauth = '' 35 | 36 | #----------------------------------------------------------------------- 37 | def __init__(self): 38 | "" 39 | pass 40 | 41 | 42 | #----------------------------------------------------------------------- 43 | def proxy_digest_authentication(self, connection): 44 | self._digest_authentication(connection, 'Proxy-', 'Proxy-') 45 | #----------------------------------------------------------------------- 46 | def www_digest_authentication(self, connection): 47 | self._digest_authentication(connection, '', 'WWW-') 48 | 49 | #----------------------------------------------------------------------- 50 | def _digest_authentication(self, connection, clientheaderprefix = '', serverheaderprefix = ''): 51 | "" 52 | connection.logger.log('*** Authorization in progress...\n') 53 | connection.close_rserver() 54 | # build an environment 55 | env = self.build_env_dict(connection) 56 | 57 | auth = connection.rserver_head_obj.get_param_values(serverheaderprefix + 'Authenticate') 58 | if auth: 59 | msg2 = auth[0][len('Digest '):] 60 | connection.logger_auth.log(digest_messages.debug_message2(msg2)) 61 | nonce = digest_messages.parse_message2(msg2) 62 | Digest_msg3 = digest_messages.create_message3(nonce, env, connection.client_head_obj) 63 | connection.logger_auth.log(digest_messages.debug_message3(Digest_msg3)) 64 | else: 65 | Digest_msg3 = '' 66 | 67 | self.lastauth = 'Digest ' + Digest_msg3 68 | tmp_client_head_obj = connection.client_head_obj.copy() 69 | tmp_client_head_obj.replace_param_value(clientheaderprefix + 'Authorization', self.lastauth) 70 | 71 | connection.logger.log('*** Connecting to Proxy for Digest request...') 72 | connection.connect_rserver() 73 | connection.reset_rserver() 74 | connection.rserver_buffer = '' 75 | connection.logger.log('*** Sending Digest header (not body) with Msg3...') 76 | tmp_client_head_obj.send(connection.rserver_socket) 77 | connection.logger.log('Done.\n') 78 | connection.logger.log('*** Digest header with Msg3:\n=====\n' + tmp_client_head_obj.__repr__()) 79 | 80 | # upon exit all the remote server variables are reset 81 | # so new remote server response will be taken by the usual way in connection.run() 82 | connection.logger.log('*** End of Digest authorization process.\n') 83 | 84 | #----------------------------------------------------------------------- 85 | def build_env_dict(self, connection): 86 | "" 87 | connection.logger.log('*** Building environment for Digest.\n') 88 | 89 | env = {} 90 | 91 | env['USER'] = connection.config['DIGEST_AUTH']['USER'] 92 | env['PASSWORD'] = connection.config['DIGEST_AUTH']['PASSWORD'] 93 | 94 | connection.logger.log('*** Digest User: %s\n' % (env['USER'],)) 95 | 96 | connection.logger.log('*** Environment has been built successfully.\n') 97 | 98 | return env 99 | 100 | -------------------------------------------------------------------------------- /ntlmaps/utils.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import string 21 | 22 | hd = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',] 23 | 24 | #-------------------------------------------------------------------------------------------- 25 | def str2hex_num(str): 26 | res = 0L 27 | for i in str: 28 | res = res << 8 29 | res = res + long(ord(i)) 30 | return hex(res) 31 | 32 | #-------------------------------------------------------------------------------------------- 33 | def str2hex(str, delimiter=''): 34 | res = '' 35 | for i in str: 36 | res = res + hd[ord(i)/16] 37 | res = res + hd[ord(i) - ((ord(i)/16) * 16)] 38 | res = res + delimiter 39 | return res 40 | 41 | #-------------------------------------------------------------------------------------------- 42 | def str2dec(str, delimiter=''): 43 | res = '' 44 | for i in str: 45 | res = res + '%3d' % ord(i) 46 | res = res + delimiter 47 | return res 48 | 49 | 50 | #-------------------------------------------------------------------------------------------- 51 | def hex2str(hex_str): 52 | res = '' 53 | for i in range(0, len(hex_str), 2): 54 | res = res + (chr(hd.index(hex_str[i]) * 16 + hd.index(hex_str[i+1]))) 55 | return res 56 | 57 | #-------------------------------------------------------------------------------------------- 58 | def str2prn_str(bin_str, delimiter=''): 59 | "" 60 | res = '' 61 | for i in bin_str: 62 | if ord(i) > 31: res = res + i 63 | else: res = res + '.' 64 | res = res + delimiter 65 | return res 66 | 67 | #-------------------------------------------------------------------------------------------- 68 | def byte2bin_str(char): 69 | "" 70 | res = '' 71 | t = ord(char) 72 | while t > 0: 73 | t1 = t / 2 74 | if t != 2 * t1: res = '1' + res 75 | else: res = '0' + res 76 | t = t1 77 | if len(res) < 8: res = '0' * (8 - len(res)) + res 78 | 79 | return res 80 | 81 | #-------------------------------------------------------------------------------------------- 82 | def str2lst(str): 83 | res = [] 84 | for i in str: 85 | res.append(ord(i)) 86 | return res 87 | 88 | #-------------------------------------------------------------------------------------------- 89 | def lst2str(lst): 90 | res = '' 91 | for i in lst: 92 | res = res + chr(i & 0xFF) 93 | return res 94 | 95 | #-------------------------------------------------------------------------------------------- 96 | def int2chrs(number_int): 97 | "" 98 | return chr(number_int & 0xFF) + chr((number_int >> 8) & 0xFF) 99 | 100 | #-------------------------------------------------------------------------------------------- 101 | def bytes2int(bytes): 102 | "" 103 | return ord(bytes[1]) * 256 + ord(bytes[0]) 104 | 105 | #-------------------------------------------------------------------------------------------- 106 | def int2hex_str(number_int16): 107 | "" 108 | res = '0x' 109 | ph = int(number_int16) / 256 110 | res = res + hd[ph/16] 111 | res = res + hd[ph - ((ph/16) * 16)] 112 | 113 | pl = int(number_int16) - (ph * 256) 114 | res = res + hd[pl/16] 115 | res = res + hd[pl - ((pl/16) * 16)] 116 | 117 | return res 118 | 119 | #-------------------------------------------------------------------------------------------- 120 | def str2unicode(string): 121 | "converts ascii string to dumb unicode" 122 | res = '' 123 | for i in string: 124 | res = res + i + '\000' 125 | return res 126 | 127 | -------------------------------------------------------------------------------- /packaging/ntlmaps.nsi: -------------------------------------------------------------------------------- 1 | ; This file is Copyright 2005 Mario Zoppetti, and was added by 2 | ; Darryl A. Dixon to 3 | ; 'NTLM Authorization Proxy Server', 4 | ; Copyright 2001 Dmitry A. Rozmanov 5 | ; 6 | ; NTLM APS is free software; you can redistribute it and/or modify 7 | ; it under the terms of the GNU General Public License as published by 8 | ; the Free Software Foundation; either version 2 of the License, or 9 | ; (at your option) any later version. 10 | ; 11 | ; NTLM APS is distributed in the hope that it will be useful, 12 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ; GNU General Public License for more details. 15 | ; 16 | ; You should have received a copy of the GNU General Public License 17 | ; along with the sofware; see the file COPYING. If not, write to the 18 | ; Free Software Foundation, Inc., 19 | ; 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 20 | 21 | ; HM NIS Edit Wizard helper defines 22 | !define PRODUCT_NAME "ntlmaps" 23 | !define PRODUCT_VERSION "1.0" 24 | !define PRODUCT_PUBLISHER "MZ" 25 | !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" 26 | !define PRODUCT_UNINST_ROOT_KEY "HKLM" 27 | 28 | Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" 29 | OutFile "ntlmaps-setup-${PRODUCT_VERSION}.exe" 30 | SetCompressor lzma 31 | 32 | LoadLanguageFile "${NSISDIR}\Contrib\Language files\English.nlf" 33 | InstallDir "$PROGRAMFILES\${PRODUCT_NAME}" 34 | Icon "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico" 35 | UninstallIcon "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" 36 | DirText "Setup will install $(^Name) in the following folder.$\r$\n$\r$\nTo install in a different folder, click Browse and select another folder." 37 | LicenseText "If you accept all the terms of the agreement, choose I Agree to continue. You must accept the agreement to install $(^Name)." 38 | LicenseData "..\COPYING" 39 | ShowInstDetails show 40 | ShowUnInstDetails show 41 | 42 | Section "MainSection" SEC01 43 | SetOutPath "$INSTDIR" 44 | SetOverwrite ifnewer 45 | ClearErrors 46 | FileOpen $0 "${PRODUCT_NAME}.cmd" w 47 | IfErrors done 48 | FileWrite $0 "${PRODUCT_NAME}.exe -c server.cfg" 49 | FileClose $0 50 | done: 51 | 52 | CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}" 53 | CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.cmd" 54 | CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.cmd" 55 | 56 | File "..\dist\bz2.pyd" 57 | File "..\dist\library.zip" 58 | File "..\dist\ntlmaps.exe" 59 | File "..\dist\ntlmaps-hashes.exe" 60 | File "..\dist\python25.dll" 61 | File "..\dist\pywintypes25.dll" 62 | File "..\dist\select.pyd" 63 | File "..\dist\server.cfg" 64 | File "..\dist\unicodedata.pyd" 65 | File "..\dist\w9xpopen.exe" 66 | File "..\dist\win32console.pyd" 67 | File "..\dist\_socket.pyd" 68 | File "..\dist\_ssl.pyd" 69 | SectionEnd 70 | 71 | Section -AdditionalIcons 72 | CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall ${PRODUCT_NAME}.lnk" "$INSTDIR\uninst.exe" 73 | SectionEnd 74 | 75 | Section -Post 76 | WriteUninstaller "$INSTDIR\uninst.exe" 77 | WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" 78 | WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe" 79 | WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_VERSION}.cmd" 80 | WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" 81 | WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" 82 | SectionEnd 83 | 84 | 85 | Function un.onUninstSuccess 86 | HideWindow 87 | MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." 88 | FunctionEnd 89 | 90 | Function un.onInit 91 | MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2 92 | Abort 93 | FunctionEnd 94 | 95 | Section Uninstall 96 | Delete "$INSTDIR\uninst.exe" 97 | Delete "$INSTDIR\_ssl.pyd" 98 | Delete "$INSTDIR\_socket.pyd" 99 | Delete "$INSTDIR\zlib.pyd" 100 | Delete "$INSTDIR\w9xpopen.exe" 101 | Delete "$INSTDIR\unicodedata.pyd" 102 | Delete "$INSTDIR\server.cfg" 103 | Delete "$INSTDIR\select.pyd" 104 | Delete "$INSTDIR\python24.dll" 105 | Delete "$INSTDIR\${PRODUCT_NAME}.exe" 106 | Delete "$INSTDIR\library.zip" 107 | Delete "$INSTDIR\bz2.pyd" 108 | Delete "$INSTDIR\${PRODUCT_NAME}.cmd" 109 | 110 | Delete "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall ${PRODUCT_NAME}.lnk" 111 | Delete "$DESKTOP\${PRODUCT_NAME}.lnk" 112 | Delete "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" 113 | RMDir "$SMPROGRAMS\${PRODUCT_NAME}" 114 | RMDir "$INSTDIR" 115 | 116 | DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" 117 | SetAutoClose true 118 | SectionEnd 119 | -------------------------------------------------------------------------------- /doc/README.txt: -------------------------------------------------------------------------------- 1 | README file for 'Digest Authorization Proxy Server' 2 | 3 | This project was forked from NTLMAPS, the NTLM Authorization Proxy Server. 4 | It adds digest authentication. Please do not ask for help on NTLM. 5 | 6 | Below is the documentation of NTLMAPS: 7 | 8 | 9 | README file for 'NTLM Authorization Proxy Server' 10 | 11 | Please read this file to the end, it contains useful installation 12 | instructions and other information. 13 | 14 | If something goes wrong, please log a support request on the 15 | sourceforge.net project page. 16 | 17 | 1. WHAT IS 'NTLM Authorization Proxy Server'? 18 | --------------------------------------------- 19 | 20 | 'NTLM Authorization Proxy Server' is a proxy-like software, that will authorize you 21 | at MS proxy server and at web servers (ISS especially) using MS proprietary NTLM 22 | authorization method and it can change some values in your client's request header 23 | so that those requests will look like ones made by MS IE. It is written in Python 24 | language. See www.python.org. 25 | 26 | 2. WHY MAY I WANT TO USE 'NTLM Authorization Proxy Server'? 27 | ----------------------------------------------------------- 28 | When your web browser uses proxy server to surf the Web from within your local 29 | network your may be required to authenticate youself to use proxy. There are 30 | two methods to do this: using 'Basic' authorization method and 'Digest' method. 31 | 32 | 'Basic' is very basic;) and not secure, but considered often as sufficient to check 33 | users inside your local network. The second - 'Digest' - is quite secure but, as I 34 | could note, use of it considered as 'too much' in order to protect your own proxy 35 | server from your own people. Well, 'Digest' is far more complicate method and I don't 36 | know any software that can use it, I think it is becuse this method simply 'overkill' for 37 | most cases. 'Basic' method is quite common and is standard method to authenticate 38 | yourself at proxies. 39 | 40 | But MicroSoft, being MS, has developed instead of standard methods two its own 41 | authentication methods - 'NTLM' (NT LanManager) and 'Negotiate'. I don't know anything 42 | about the second one, but I spend some time on 'NTLM'. 43 | 44 | Because NTLM (NT LanManager) is a proprietary algorithm it is looks like that 45 | only MS's products can use it (IE3+ as far as I know). And it gives you a bunch of 46 | fun things: If proxy server that you have to use is set so it requres your to 47 | authenticate yourself with 'NTLM' algorithm then you will be able to use ONLY MS's 48 | products with this proxy server. No ReGet, no WGET, no Junkbuster, no Netscape 49 | Navigator, no Lynx or your local squid and no lots of other good software. 50 | 51 | Why admin of proxy server in your local network may want to ban standard 'Basic' 52 | and require 'NTLM'? I don't know. May be because he likes MS too much;). 53 | 54 | 55 | 3. HISTORY BEHIND THE 'NTLM Authorization Proxy Server'. 56 | -------------------------------------------------------- 57 | 58 | I got the idea for 'NTLM Authorization Proxy Server' when admin in our network 59 | swiched MS Proxy so that we had to use 'NTLM'. It was simply disaster. Everything 60 | stopped working but IE5.5. 61 | 62 | So then I had realized that there was no solution to make my favorite programs work with 63 | it I decided to do something. And here you are. 64 | 65 | 66 | 4. REQUIREMENTS 67 | --------------- 68 | 69 | You will need a thread-safe Python installation, version 1.5.2 or later. 70 | Python is available from http://www.python.org/ . Get it, install it, and you 71 | are ready to try. Python also comes with all GNU/Linux distributions that 72 | I am aware of, so if you are a GNU/Linux user you should have that in your 73 | box already. 74 | 75 | Please note that I have not test it with Python versions higher than 1.5.2. 76 | Therefore you may have problems with later Pythons, but I hope you will not. 77 | 78 | 5. LICENSING & PRICING 79 | ---------------------- 80 | 81 | 'NTLM Authorization Proxy Server' is distributed under the GNU General 82 | Public License; either version 2 of the License which is included in 83 | this archive (see file COPYING), or (at your option) any later 84 | version. 85 | 86 | 87 | The above mean that 'NTLM Authorization Proxy Server' is pretty much free. 88 | You have to pay nothing for it. 89 | 90 | 6. CREDITS 91 | ---------- 92 | Thanks to Nathan Lineback for reporting about persistent 93 | connection to MS server with multiple authorization requests. I had never heard of 94 | such a behavior before that. 95 | 96 | Janek Schwarz added command line option '-c' for config files 97 | other than default server.cfg in working directory. Thanks for that to him. 98 | 99 | Thanks to Stephen D. Cohen for very useful suggestions and cooperation. 100 | 101 | Patch for Python >=1.6 by Markus Indenbirken (markus_i@gmx.de) 102 | 103 | The most useful info has been got from "NTLM Authentication Scheme for HTTP" 104 | page by Ronald Tschalar (ronald@innovation.ch). I included the page in the distribution 105 | because it looks like that the server it contains is going down. 106 | (http://www.innovation.ch/java/ntlm.html) 107 | 108 | md4.py and des_c.py had been translated from md4.c and des.c from 109 | "the Python Cryptography Toolkit, version 1.0.0 Copyright (C) 1995, A.M. Kuchling" 110 | 111 | -------------------------------------------------------------------------------- /ntlmaps/monitor_upstream.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright 2004 Darryl A. Dixon 2 | # and is part of 'NTLM Authorization Proxy Server', 3 | # Copyright 2001 Dmitry A. Rozmanov 4 | # 5 | # NTLM APS is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # NTLM APS is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with the sofware; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 18 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 19 | # 20 | 21 | import signal, httplib, time, socket, thread, os 22 | 23 | #-------------------------------------------------------------- 24 | class monitorThread: 25 | 26 | #-------------------------------------------------------------- 27 | def __init__(self, config, die_sig=signal.SIGINT): 28 | self.alive = 1 29 | self.config = config 30 | self.die_sig = die_sig 31 | self.threadsToKill = [] 32 | self.timeoutSeconds = self.config['GENERAL']['PARENT_PROXY_TIMEOUT'] 33 | 34 | #-------------------------------------------------------------- 35 | def run(self): 36 | while self.alive: 37 | self.alarmThread = timerThread(self.timeoutSeconds, self.alive, self.die_sig) 38 | thread.start_new_thread(self.alarmThread.run, ()) 39 | # We poll the current proxy for responsiveness... 40 | # TODO: add logger entries for all these exceptions 41 | self.httpTest() 42 | self.alarmThread.alive = 0 43 | time.sleep(self.timeoutSeconds+1) 44 | # Maximum timeout before hitting bottom of this loop is therefore 45 | # at most self.timeoutSeconds*2+1 and at least self.timeoutSeconds+1. 46 | # Hopefully this is a reasonable tradeoff between noticeable 47 | # service outage for user and creaming the proxy with stacks of 48 | # our spurious requests... :) 49 | 50 | def die(self): 51 | try: 52 | self.alarmThread.alive = 0 53 | except AttributeError: 54 | pass # self.alarmThread is already dead 55 | self.alive = 0 56 | die_func = signal.getsignal(self.die_sig) 57 | die_func(self.die_sig) 58 | 59 | def httpTest(self): 60 | """ 61 | Although the httplib.HTTP class is retained in Python 2.x for backwards 62 | compatability with Python 1.5 it is not recommended for new code, and as 63 | most users will now be using the Python 2.x stream I have abstracted away 64 | two code paths in here. The maintenance will be slightly higher, but in 65 | the long term it will make it easier to fork off a Python 1.5.2 maintenance 66 | version of ntlmaps and just keep the pure Python 2.x optimised version. 67 | """ 68 | if 'HTTPConnection' in dir(httplib): 69 | # Python 2.x 70 | try: 71 | conn = httplib.HTTPConnection(self.config['GENERAL']['PARENT_PROXY'], self.config['GENERAL']['PARENT_PROXY_PORT']) 72 | try: 73 | conn.request("GET", "/") 74 | try: 75 | data = conn.getresponse() 76 | try: 77 | if not data.read(): # Got a b0rked response? 78 | self.die() 79 | except AssertionError: # Yup, got a wacky response 80 | self.die() 81 | conn.close() 82 | except (AttributeError, httplib.BadStatusLine): # Didn't somehow connect? 83 | self.die() 84 | except socket.error: # Service not running/listening on specified port? 85 | self.die() 86 | except socket.gaierror: # Name resolution error for this proxy? 87 | self.die() 88 | else: 89 | # Python 1.5.2 90 | try: 91 | # This call fails far more regularly than it should. Best to move to 92 | # using Python 2.x 93 | conn = httplib.HTTP(self.config['GENERAL']['PARENT_PROXY'], self.config['GENERAL']['PARENT_PROXY_PORT']) 94 | try: 95 | conn.putrequest('GET', '/') 96 | conn.endheaders() 97 | try: 98 | errcode, errmsg, headers = conn.getreply() 99 | try: 100 | data = conn.getfile() 101 | if not data.read(): 102 | self.die() 103 | data.close() 104 | except: 105 | self.die() 106 | except: 107 | self.die() 108 | except: 109 | self.die() 110 | except: 111 | self.die() 112 | return 113 | 114 | #-------------------------------------------------------------- 115 | class timerThread: 116 | """ 117 | Used in place of SIGALRM as Windows doesn't support it. 118 | """ 119 | 120 | #-------------------------------------------------------------- 121 | def __init__(self, seconds, parentAlive, die_sig=signal.SIGINT): 122 | self.seconds = seconds 123 | self.parentAlive = parentAlive 124 | self.die_sig = die_sig 125 | self.alive = 1 126 | 127 | #-------------------------------------------------------------- 128 | def run(self): 129 | time.sleep(self.seconds) 130 | if self.alive and self.parentAlive: 131 | die_func = signal.getsignal(self.die_sig) 132 | die_func(self.die_sig) 133 | -------------------------------------------------------------------------------- /ntlmaps/config_affairs.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import socket, thread, string, sys 21 | import logger 22 | 23 | #------------------------------------------------------------------------- 24 | def arrange(conf): 25 | 26 | #----------------------------------------------- 27 | # GENERAL 28 | conf['GENERAL']['PARENT_PROXY'] 29 | 30 | # If we do not use PARENT_PROXY then there are some items we just don't need: 31 | if conf['GENERAL']['PARENT_PROXY']: 32 | conf['GENERAL']['AVAILABLE_PROXY_LIST'] = string.split(conf['GENERAL']['PARENT_PROXY']) 33 | conf['GENERAL']['PARENT_PROXY'] = conf['GENERAL']['AVAILABLE_PROXY_LIST'].pop() 34 | conf['GENERAL']['PARENT_PROXY_PORT'] = makeInt(conf['GENERAL']['PARENT_PROXY_PORT'], 'PARENT_PROXY_PORT') 35 | conf['GENERAL']['PARENT_PROXY_TIMEOUT'] = makeInt(conf['GENERAL']['PARENT_PROXY_TIMEOUT'], 'PARENT_PROXY_TIMEOUT') 36 | conf['GENERAL']['HOSTS_TO_BYPASS_PARENT_PROXY'] = string.split(conf['GENERAL']['HOSTS_TO_BYPASS_PARENT_PROXY']) 37 | conf['GENERAL']['DIRECT_CONNECT_IF_POSSIBLE'] = makeInt(conf['GENERAL']['DIRECT_CONNECT_IF_POSSIBLE'], 'DIRECT_CONNECT_IF_POSSIBLE') 38 | try: 39 | conf['GENERAL']['MAX_CONNECTION_BACKLOG'] = int(conf['GENERAL']['MAX_CONNECTION_BACKLOG']) 40 | except ValueError: 41 | if conf['GENERAL']['MAX_CONNECTION_BACKLOG'] == 'SOMAXCONN': 42 | conf['GENERAL']['MAX_CONNECTION_BACKLOG'] = socket.SOMAXCONN 43 | else: 44 | print "ERROR: There is a problem with 'MAX_CONNECTION_BACKLOG' in the config (neither a number nor 'SOMAXCONN'?)" 45 | sys.exit(1) 46 | conf['GENERAL']['LISTEN_PORT'] = makeInt(conf['GENERAL']['LISTEN_PORT'], 'LISTEN_PORT') 47 | 48 | conf['GENERAL']['ALLOW_EXTERNAL_CLIENTS'] = makeInt(conf['GENERAL']['ALLOW_EXTERNAL_CLIENTS'], 'ALLOW_EXTERNAL_CLIENTS') 49 | hostname = socket.gethostname() 50 | conf['GENERAL']['HOST'] = hostname 51 | try: 52 | externalIP = socket.gethostbyname_ex(hostname)[2] 53 | except socket.error: # Python 1.5 54 | print "ERROR: Unable to get the IP address of this machine. This is not a fatal problem, but may cause problems for you using this proxy in some scenarios." 55 | externalIP = [] 56 | except socket.gaierror: # Python 2.x 57 | print "ERROR: Unable to get the IP address of this machine. This is not a fatal problem, but may cause problems for you using this proxy in some scenarios." 58 | externalIP = [] 59 | conf['GENERAL']['HOST_IP_LIST'] = externalIP + ['127.0.0.1'] 60 | 61 | conf['GENERAL']['FRIENDLY_IPS'] = conf['GENERAL']['HOST_IP_LIST'] + string.split(conf['GENERAL']['FRIENDLY_IPS']) 62 | 63 | # Idea contributed by Fernando M. Garcia Garcia: 64 | saneList = [] 65 | for host in conf['GENERAL']['FRIENDLY_IPS']: 66 | try: 67 | saneList.append(socket.gethostbyname(host)) 68 | except (socket.error): #socket.gaierror on Python 2.x 69 | print "ERROR: Could not get IP address for %s in list of FRIENDLY_IPS" % host 70 | sys.exit(1) 71 | conf['GENERAL']['FRIENDLY_IPS'] = saneList 72 | 73 | conf['GENERAL']['URL_LOG'] = makeInt(conf['GENERAL']['URL_LOG'], 'URL_LOG') 74 | url_logger = logger.Logger('url.log', conf['GENERAL']['URL_LOG']) 75 | url_logger_lock = thread.allocate_lock() 76 | conf['GENERAL']['URL_LOGGER'] = url_logger 77 | conf['GENERAL']['URL_LOG_LOCK'] = url_logger_lock 78 | 79 | 80 | #----------------------------------------------- 81 | # NTLM_AUTH 82 | if not conf['NTLM_AUTH'].has_key('NTLM_FLAGS'): 83 | conf['NTLM_AUTH']['NTLM_FLAGS'] = '' 84 | #conf['NTLM']['FULL_NTLM'] = makeInt(conf['NTLM']['FULL_NTLM'], 'FULL_NTLM') 85 | conf['NTLM_AUTH']['LM_PART'] = makeInt(conf['NTLM_AUTH']['LM_PART'], 'LM_PART') 86 | conf['NTLM_AUTH']['NT_PART'] = makeInt(conf['NTLM_AUTH']['NT_PART'], 'NT_PART') 87 | conf['NTLM_AUTH']['NTLM_TO_BASIC'] = makeInt(conf['NTLM_AUTH']['NTLM_TO_BASIC'], 'NTLM_TO_BASIC') 88 | if not conf['NTLM_AUTH']['NT_DOMAIN']: 89 | print "ERROR: NT DOMAIN must be set." 90 | sys.exit(1) 91 | if not conf['NTLM_AUTH'].has_key('PASSWORD'): 92 | conf['NTLM_AUTH']['PASSWORD'] = '' 93 | conf['NTLM_AUTH']['COMPLEX_PASSWORD_INPUT'] = makeInt(conf['NTLM_AUTH']['COMPLEX_PASSWORD_INPUT'], 'COMPLEX_PASSWORD_INPUT') 94 | 95 | 96 | #----------------------------------------------- 97 | # DEBUG 98 | conf['DEBUG']['DEBUG'] = makeInt(conf['DEBUG']['DEBUG'], 'DEBUG') 99 | conf['DEBUG']['AUTH_DEBUG'] = makeInt(conf['DEBUG']['AUTH_DEBUG'], 'AUTH_DEBUG') 100 | conf['DEBUG']['BIN_DEBUG'] = makeInt(conf['DEBUG']['BIN_DEBUG'], 'BIN_DEBUG') 101 | conf['DEBUG']['NOTHREADS'] = makeInt(conf['DEBUG']['NOTHREADS'], 'NOTHREADS') 102 | 103 | # screen activity 104 | if conf['DEBUG'].has_key('SCR_DEBUG'): 105 | conf['DEBUG']['SCR_DEBUG'] = int(conf['DEBUG']['SCR_DEBUG']) 106 | else: 107 | conf['DEBUG']['SCR_DEBUG'] = 0 108 | 109 | return conf 110 | 111 | def makeInt(string, errorDesc='an item'): 112 | try: 113 | ret = int(string) 114 | except ValueError: 115 | print "ERROR: There is a problem with "+errorDesc+" in the config (is it not a number?)" 116 | sys.exit(1) 117 | return ret 118 | -------------------------------------------------------------------------------- /ntlmaps/server.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import socket, thread, sys, signal, getpass, base64 21 | import proxy_client, monitor_upstream, ntlm_procs 22 | 23 | #-------------------------------------------------------------- 24 | class AuthProxyServer: 25 | 26 | #-------------------------------------------------------------- 27 | def __init__(self, config): 28 | self.config = config 29 | self.MyHost = '' 30 | self.ListenPort = self.config['GENERAL']['LISTEN_PORT'] 31 | self.sigLock = thread.allocate_lock() # For locking in the sigHandler 32 | self.monLock = thread.allocate_lock() # For keeping the monitor thread sane 33 | self.watchUpstream = 0 34 | self.monitor = None 35 | password_prompt = getpass.getpass 36 | # If the hashes exist then use them. 37 | if self.config['NTLM_AUTH']['LM_HASHED_PW'] and self.config['NTLM_AUTH']['NT_HASHED_PW']: 38 | self.config['NTLM_AUTH']['LM_HASHED_PW'] = base64.decodestring(self.config['NTLM_AUTH']['LM_HASHED_PW']) 39 | self.config['NTLM_AUTH']['NT_HASHED_PW'] = base64.decodestring(self.config['NTLM_AUTH']['NT_HASHED_PW']) 40 | else: 41 | if self.config['NTLM_AUTH']['COMPLEX_PASSWORD_INPUT']: 42 | try: 43 | import win32console 44 | password_prompt = win32console.getpass 45 | except ImportError: 46 | sys.stderr.write('Unable to load win32console support; complex passwords can not be input.\n') 47 | except AttributeError: 48 | sys.stderr.write('win32console lacking getpass support; complex passwords can not be input.\n') 49 | if not self.config['NTLM_AUTH']['NTLM_TO_BASIC']: 50 | if not self.config['NTLM_AUTH']['PASSWORD']: 51 | tries = 3 52 | print '------------------------' 53 | while tries and (not self.config['NTLM_AUTH']['PASSWORD']): 54 | tries = tries - 1 55 | self.config['NTLM_AUTH']['PASSWORD'] = password_prompt('Your NT password to be used:') 56 | if not self.config['NTLM_AUTH']['PASSWORD']: 57 | print 'Sorry. PASSWORD is required, bye.' 58 | sys.exit(1) 59 | else: 60 | # TODO: migrate this properly so placeholders aren't required 61 | self.config['NTLM_AUTH']['USER'] = 'placeholder_username' 62 | self.config['NTLM_AUTH']['PASSWORD'] = 'placeholder_password' 63 | # hashed passwords calculation 64 | self.config['NTLM_AUTH']['LM_HASHED_PW'] = ntlm_procs.create_LM_hashed_password(self.config['NTLM_AUTH']['PASSWORD']) 65 | self.config['NTLM_AUTH']['NT_HASHED_PW'] = ntlm_procs.create_NT_hashed_password(self.config['NTLM_AUTH']['PASSWORD']) 66 | 67 | #-------------------------------------------------------------- 68 | def run(self): 69 | signal.signal(signal.SIGINT, self.sigHandler) 70 | if self.config['GENERAL']['PARENT_PROXY'] and self.config['GENERAL']['AVAILABLE_PROXY_LIST']: 71 | self.watchUpstream = 1 72 | self.monitor = monitor_upstream.monitorThread(self.config, signal.SIGINT) 73 | thread.start_new_thread(self.monitor.run, ()) 74 | try: 75 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 76 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 77 | s.bind((self.MyHost, self.ListenPort)) 78 | except socket.error: 79 | print "ERROR: Could not create socket. Possibly port %s is still being used by another process." % self.config['GENERAL']['LISTEN_PORT'] 80 | sys.exit(1) 81 | print 'Now listening at %s on port %s' % (self.config['GENERAL']['HOST'], self.config['GENERAL']['LISTEN_PORT']) 82 | while(1): 83 | s.listen(self.config['GENERAL']['MAX_CONNECTION_BACKLOG']) 84 | try: 85 | conn, addr = s.accept() 86 | if self.config['GENERAL']['ALLOW_EXTERNAL_CLIENTS']: 87 | self.client_run(conn, addr) 88 | else: 89 | if addr[0] in self.config['GENERAL']['FRIENDLY_IPS']: 90 | self.client_run(conn, addr) 91 | else: 92 | conn.close() 93 | except socket.error: 94 | pass 95 | s.close() 96 | 97 | #-------------------------------------------------------------- 98 | def client_run(self, conn, addr): 99 | if self.watchUpstream: 100 | c = proxy_client.proxy_HTTP_Client(conn, addr, self.config, self.watchUpstream, self.monLock, self.monitor.threadsToKill) 101 | else: 102 | c = proxy_client.proxy_HTTP_Client(conn, addr, self.config, self.watchUpstream) 103 | if self.config['DEBUG'].get('NOTHREADS'): 104 | c.run() 105 | else: 106 | thread.start_new_thread(c.run, ()) 107 | 108 | #-------------------------------------------------------------- 109 | def sigHandler(self, signum=None, frame=None): 110 | if signum == signal.SIGINT: 111 | if self.watchUpstream: 112 | if self.sigLock.acquire(0): 113 | old_monitor = self.monitor 114 | self.config['GENERAL']['AVAILABLE_PROXY_LIST'].insert(0, self.config['GENERAL']['PARENT_PROXY']) 115 | self.monLock.acquire() # Keep locked section as small as possible 116 | self.config['GENERAL']['PARENT_PROXY'] = self.config['GENERAL']['AVAILABLE_PROXY_LIST'].pop() 117 | self.monitor = monitor_upstream.monitorThread(self.config, signal.SIGINT) 118 | self.monLock.release() 119 | print "Moving to proxy server: "+self.config['GENERAL']['PARENT_PROXY'] 120 | old_monitor.alive = 0 121 | thread.start_new_thread(self.monitor.run, ()) 122 | map(lambda x: x.exit(), old_monitor.threadsToKill) 123 | old_monitor.die() # Protected from recursion by lock 124 | self.sigLock.release() 125 | else: 126 | # SIGINT is only special if we are in upstream mode: 127 | print 'Got SIGINT, exiting now...' 128 | sys.exit(1) 129 | else: 130 | print 'Got SIGNAL '+str(signum)+', exiting now...' 131 | sys.exit(1) 132 | return 133 | -------------------------------------------------------------------------------- /ntlmaps/md4.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | # MD4 validation data 21 | 22 | md4_test= [ 23 | ('', 0x31d6cfe0d16ae931b73c59d7e0c089c0L), 24 | ("a", 0xbde52cb31de33e46245e05fbdbd6fb24L), 25 | ("abc", 0xa448017aaf21d8525fc10ae87aa6729dL), 26 | ("message digest", 0xd9130a8164549fe818874806e1c7014bL), 27 | ("abcdefghijklmnopqrstuvwxyz", 0xd79e1c308aa5bbcdeea8ed63df412da9L), 28 | ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 29 | 0x043f8582f241db351ce627e153e7f0e4L), 30 | ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", 31 | 0xe33b4ddc9c38f2199c3e7b164fcc0536L), 32 | ] 33 | 34 | #------------------------------------------------------------------------------ 35 | from U32 import U32 36 | 37 | class MD4: 38 | A = None 39 | B = None 40 | C = None 41 | D = None 42 | count, len1, len2 = None, None, None 43 | buf = [] 44 | 45 | #----------------------------------------------------- 46 | def __init__(self): 47 | 48 | 49 | self.A = U32(0x67452301L) 50 | self.B = U32(0xefcdab89L) 51 | self.C = U32(0x98badcfeL) 52 | self.D = U32(0x10325476L) 53 | self.count, self.len1, self.len2 = U32(0L), U32(0L), U32(0L) 54 | self.buf = [0x00] * 64 55 | 56 | #----------------------------------------------------- 57 | def __repr__(self): 58 | r = 'A = %s, \nB = %s, \nC = %s, \nD = %s.\n' % (self.A.__repr__(), self.B.__repr__(), self.C.__repr__(), self.D.__repr__()) 59 | r = r + 'count = %s, \nlen1 = %s, \nlen2 = %s.\n' % (self.count.__repr__(), self.len1.__repr__(), self.len2.__repr__()) 60 | for i in range(4): 61 | for j in range(16): 62 | r = r + '%4s ' % hex(self.buf[i+j]) 63 | r = r + '\n' 64 | 65 | return r 66 | #----------------------------------------------------- 67 | def make_copy(self): 68 | 69 | dest = new() 70 | 71 | dest.len1 = self.len1 72 | dest.len2 = self.len2 73 | dest.A = self.A 74 | dest.B = self.B 75 | dest.C = self.C 76 | dest.D = self.D 77 | dest.count = self.count 78 | for i in range(self.count): 79 | dest.buf[i] = self.buf[i] 80 | 81 | return dest 82 | 83 | #----------------------------------------------------- 84 | def update(self, str): 85 | 86 | buf = [] 87 | for i in str: buf.append(ord(i)) 88 | ilen = U32(len(buf)) 89 | #print (ilen) 90 | 91 | # --NON ASCII COMMENT ELIDED-- 92 | if (long(self.len1 + (ilen << 3)) < long(self.len1)): 93 | self.len2 = self.len2 + U32(1) 94 | 95 | self.len1 = self.len1 + (ilen << 3) 96 | self.len2 = self.len2 + (ilen >> 29) 97 | # print int(self.len1), int(self.len2) 98 | #print (self.len1), (self.len2) 99 | 100 | L = U32(0) 101 | bufpos = 0 102 | while (long(ilen) > 0): 103 | if (64 - long(self.count)) < long(ilen): L = U32(64 - long(self.count)) 104 | else: L = ilen 105 | for i in range(int(L)): self.buf[i + int(self.count)] = buf[i + bufpos] 106 | self.count = self.count + L 107 | ilen = ilen - L 108 | bufpos = bufpos + int(L) 109 | 110 | #print self.count, L, ilen 111 | if (long(self.count) == 64L): 112 | self.count = U32(0L) 113 | X = [] 114 | i = 0 115 | for j in range(16): 116 | X.append(U32(self.buf[i]) + (U32(self.buf[i+1]) << 8) + \ 117 | (U32(self.buf[i+2]) << 16) + (U32(self.buf[i+3]) << 24)) 118 | i = i + 4 119 | 120 | A = self.A 121 | B = self.B 122 | C = self.C 123 | D = self.D 124 | 125 | A = f1(A,B,C,D, 0, 3, X) 126 | D = f1(D,A,B,C, 1, 7, X) 127 | C = f1(C,D,A,B, 2,11, X) 128 | B = f1(B,C,D,A, 3,19, X) 129 | A = f1(A,B,C,D, 4, 3, X) 130 | D = f1(D,A,B,C, 5, 7, X) 131 | C = f1(C,D,A,B, 6,11, X) 132 | B = f1(B,C,D,A, 7,19, X) 133 | A = f1(A,B,C,D, 8, 3, X) 134 | D = f1(D,A,B,C, 9, 7, X) 135 | C = f1(C,D,A,B,10,11, X) 136 | B = f1(B,C,D,A,11,19, X) 137 | A = f1(A,B,C,D,12, 3, X) 138 | D = f1(D,A,B,C,13, 7, X) 139 | C = f1(C,D,A,B,14,11, X) 140 | B = f1(B,C,D,A,15,19, X) 141 | 142 | A = f2(A,B,C,D, 0, 3, X) 143 | D = f2(D,A,B,C, 4, 5, X) 144 | C = f2(C,D,A,B, 8, 9, X) 145 | B = f2(B,C,D,A,12,13, X) 146 | A = f2(A,B,C,D, 1, 3, X) 147 | D = f2(D,A,B,C, 5, 5, X) 148 | C = f2(C,D,A,B, 9, 9, X) 149 | B = f2(B,C,D,A,13,13, X) 150 | A = f2(A,B,C,D, 2, 3, X) 151 | D = f2(D,A,B,C, 6, 5, X) 152 | C = f2(C,D,A,B,10, 9, X) 153 | B = f2(B,C,D,A,14,13, X) 154 | A = f2(A,B,C,D, 3, 3, X) 155 | D = f2(D,A,B,C, 7, 5, X) 156 | C = f2(C,D,A,B,11, 9, X) 157 | B = f2(B,C,D,A,15,13, X) 158 | 159 | A = f3(A,B,C,D, 0, 3, X) 160 | D = f3(D,A,B,C, 8, 9, X) 161 | C = f3(C,D,A,B, 4,11, X) 162 | B = f3(B,C,D,A,12,15, X) 163 | A = f3(A,B,C,D, 2, 3, X) 164 | D = f3(D,A,B,C,10, 9, X) 165 | C = f3(C,D,A,B, 6,11, X) 166 | B = f3(B,C,D,A,14,15, X) 167 | A = f3(A,B,C,D, 1, 3, X) 168 | D = f3(D,A,B,C, 9, 9, X) 169 | C = f3(C,D,A,B, 5,11, X) 170 | B = f3(B,C,D,A,13,15, X) 171 | A = f3(A,B,C,D, 3, 3, X) 172 | D = f3(D,A,B,C,11, 9, X) 173 | C = f3(C,D,A,B, 7,11, X) 174 | B = f3(B,C,D,A,15,15, X) 175 | 176 | self.A = self.A + A 177 | self.B = self.B + B 178 | self.C = self.C + C 179 | self.D = self.D + D 180 | #print self 181 | 182 | #----------------------------------------------------- 183 | def digest(self): 184 | 185 | res = [0x00] * 16 186 | s = [0x00] * 8 187 | padding = [0x00] * 64 188 | padding[0] = 0x80 189 | padlen, oldlen1, oldlen2 = U32(0), U32(0), U32(0) 190 | 191 | temp = self.make_copy() 192 | 193 | oldlen1 = temp.len1 194 | oldlen2 = temp.len2 195 | if (56 <= long(self.count)): padlen = U32(56 - long(self.count) + 64) 196 | else: padlen = U32(56 - long(self.count)) 197 | #print int(padlen) 198 | temp.update(int_array2str(padding[:int(padlen)])) 199 | #print temp 200 | 201 | s[0]= (oldlen1) & U32(0xFF) 202 | s[1]=((oldlen1) >> 8) & U32(0xFF) 203 | s[2]=((oldlen1) >> 16) & U32(0xFF) 204 | s[3]=((oldlen1) >> 24) & U32(0xFF) 205 | s[4]= (oldlen2) & U32(0xFF) 206 | s[5]=((oldlen2) >> 8) & U32(0xFF) 207 | s[6]=((oldlen2) >> 16) & U32(0xFF) 208 | s[7]=((oldlen2) >> 24) & U32(0xFF) 209 | temp.update(int_array2str(s)) 210 | 211 | #print temp 212 | 213 | res[ 0]= temp.A & U32(0xFF) 214 | res[ 1]=(temp.A >> 8) & U32(0xFF) 215 | res[ 2]=(temp.A >> 16) & U32(0xFF) 216 | res[ 3]=(temp.A >> 24) & U32(0xFF) 217 | res[ 4]= temp.B & U32(0xFF) 218 | res[ 5]=(temp.B >> 8) & U32(0xFF) 219 | res[ 6]=(temp.B >> 16) & U32(0xFF) 220 | res[ 7]=(temp.B >> 24) & U32(0xFF) 221 | res[ 8]= temp.C & U32(0xFF) 222 | res[ 9]=(temp.C >> 8) & U32(0xFF) 223 | res[10]=(temp.C >> 16) & U32(0xFF) 224 | res[11]=(temp.C >> 24) & U32(0xFF) 225 | res[12]= temp.D & U32(0xFF) 226 | res[13]=(temp.D >> 8) & U32(0xFF) 227 | res[14]=(temp.D >> 16) & U32(0xFF) 228 | res[15]=(temp.D >> 24) & U32(0xFF) 229 | 230 | return int_array2str(res) 231 | 232 | #------------------------------------------------------------------------------ 233 | def F(x, y, z): return (((x) & (y)) | ((~x) & (z))) 234 | def G(x, y, z): return (((x) & (y)) | ((x) & (z)) | ((y) & (z))) 235 | def H(x, y, z): return ((x) ^ (y) ^ (z)) 236 | 237 | def ROL(x, n): return (((x) << n) | ((x) >> (32-n))) 238 | 239 | def f1(a, b, c, d, k, s, X): return ROL(a + F(b, c, d) + X[k], s) 240 | def f2(a, b, c, d, k, s, X): return ROL(a + G(b, c, d) + X[k] + U32(0x5a827999L), s) 241 | def f3(a, b, c, d, k, s, X): return ROL(a + H(b, c, d) + X[k] + U32(0x6ed9eba1L), s) 242 | 243 | def int_array2str(array): 244 | str = '' 245 | for i in array: 246 | str = str + chr(i) 247 | return str 248 | 249 | new = MD4 250 | -------------------------------------------------------------------------------- /ntlmaps/des_c.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | from U32 import U32 21 | 22 | # --NON ASCII COMMENT ELIDED-- 23 | #typedef unsigned char des_cblock[8]; 24 | #define HDRSIZE 4 25 | 26 | def c2l(c): 27 | "char[4] to unsigned long" 28 | l = U32(c[0]) 29 | l = l | (U32(c[1]) << 8) 30 | l = l | (U32(c[2]) << 16) 31 | l = l | (U32(c[3]) << 24) 32 | return l 33 | 34 | def c2ln(c,l1,l2,n): 35 | "char[n] to two unsigned long???" 36 | c = c + n 37 | l1, l2 = U32(0), U32(0) 38 | 39 | f = 0 40 | if n == 8: 41 | l2 = l2 | (U32(c[7]) << 24) 42 | f = 1 43 | if f or (n == 7): 44 | l2 = l2 | (U32(c[6]) << 16) 45 | f = 1 46 | if f or (n == 6): 47 | l2 = l2 | (U32(c[5]) << 8) 48 | f = 1 49 | if f or (n == 5): 50 | l2 = l2 | U32(c[4]) 51 | f = 1 52 | if f or (n == 4): 53 | l1 = l1 | (U32(c[3]) << 24) 54 | f = 1 55 | if f or (n == 3): 56 | l1 = l1 | (U32(c[2]) << 16) 57 | f = 1 58 | if f or (n == 2): 59 | l1 = l1 | (U32(c[1]) << 8) 60 | f = 1 61 | if f or (n == 1): 62 | l1 = l1 | U32(c[0]) 63 | return (l1, l2) 64 | 65 | def l2c(l): 66 | "unsigned long to char[4]" 67 | c = [] 68 | c.append(int(l & U32(0xFF))) 69 | c.append(int((l >> 8) & U32(0xFF))) 70 | c.append(int((l >> 16) & U32(0xFF))) 71 | c.append(int((l >> 24) & U32(0xFF))) 72 | return c 73 | 74 | def n2l(c, l): 75 | "network to host long" 76 | l = U32(c[0] << 24) 77 | l = l | (U32(c[1]) << 16) 78 | l = l | (U32(c[2]) << 8) 79 | l = l | (U32(c[3])) 80 | return l 81 | 82 | def l2n(l, c): 83 | "host to network long" 84 | c = [] 85 | c.append(int((l >> 24) & U32(0xFF))) 86 | c.append(int((l >> 16) & U32(0xFF))) 87 | c.append(int((l >> 8) & U32(0xFF))) 88 | c.append(int((l ) & U32(0xFF))) 89 | return c 90 | 91 | def l2cn(l1, l2, c, n): 92 | "" 93 | for i in range(n): c.append(0x00) 94 | f = 0 95 | if f or (n == 8): 96 | c[7] = int((l2 >> 24) & U32(0xFF)) 97 | f = 1 98 | if f or (n == 7): 99 | c[6] = int((l2 >> 16) & U32(0xFF)) 100 | f = 1 101 | if f or (n == 6): 102 | c[5] = int((l2 >> 8) & U32(0xFF)) 103 | f = 1 104 | if f or (n == 5): 105 | c[4] = int((l2 ) & U32(0xFF)) 106 | f = 1 107 | if f or (n == 4): 108 | c[3] = int((l1 >> 24) & U32(0xFF)) 109 | f = 1 110 | if f or (n == 3): 111 | c[2] = int((l1 >> 16) & U32(0xFF)) 112 | f = 1 113 | if f or (n == 2): 114 | c[1] = int((l1 >> 8) & U32(0xFF)) 115 | f = 1 116 | if f or (n == 1): 117 | c[0] = int((l1 ) & U32(0xFF)) 118 | f = 1 119 | return c[:n] 120 | 121 | # array of data 122 | # static unsigned long des_SPtrans[8][64]={ 123 | # static unsigned long des_skb[8][64]={ 124 | from des_data import des_SPtrans, des_skb 125 | 126 | def D_ENCRYPT(tup, u, t, s): 127 | L, R, S = tup 128 | #print 'LRS1', L, R, S, u, t, '-->', 129 | u = (R ^ s[S]) 130 | t = R ^ s[S + 1] 131 | t = ((t >> 4) + (t << 28)) 132 | L = L ^ (des_SPtrans[1][int((t ) & U32(0x3f))] | \ 133 | des_SPtrans[3][int((t >> 8) & U32(0x3f))] | \ 134 | des_SPtrans[5][int((t >> 16) & U32(0x3f))] | \ 135 | des_SPtrans[7][int((t >> 24) & U32(0x3f))] | \ 136 | des_SPtrans[0][int((u ) & U32(0x3f))] | \ 137 | des_SPtrans[2][int((u >> 8) & U32(0x3f))] | \ 138 | des_SPtrans[4][int((u >> 16) & U32(0x3f))] | \ 139 | des_SPtrans[6][int((u >> 24) & U32(0x3f))]) 140 | #print 'LRS:', L, R, S, u, t 141 | return ((L, R, S), u, t, s) 142 | 143 | 144 | def PERM_OP (tup, n, m): 145 | "tup - (a, b, t)" 146 | a, b, t = tup 147 | t = ((a >> n) ^ b) & m 148 | b = b ^ t 149 | a = a ^ (t << n) 150 | return (a, b, t) 151 | 152 | def HPERM_OP (tup, n, m): 153 | "tup - (a, t)" 154 | a, t = tup 155 | t = ((a << (16 - n)) ^ a) & m 156 | a = a ^ t ^ (t >> (16 - n)) 157 | return (a, t) 158 | 159 | shifts2 = [0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0] 160 | 161 | class DES: 162 | KeySched = None # des_key_schedule 163 | 164 | def __init__(self, key_str): 165 | # key - UChar[8] 166 | key = [] 167 | for i in key_str: key.append(ord(i)) 168 | #print 'key:', key 169 | self.KeySched = des_set_key(key) 170 | #print 'schedule:', self.KeySched, len(self.KeySched) 171 | 172 | def decrypt(self, str): 173 | # block - UChar[] 174 | block = [] 175 | for i in str: block.append(ord(i)) 176 | #print block 177 | block = des_ecb_encrypt(block, self.KeySched, 0) 178 | res = '' 179 | for i in block: res = res + (chr(i)) 180 | return res 181 | 182 | def encrypt(self, str): 183 | # block - UChar[] 184 | block = [] 185 | for i in str: block.append(ord(i)) 186 | block = des_ecb_encrypt(block, self.KeySched, 1) 187 | res = '' 188 | for i in block: res = res + (chr(i)) 189 | return res 190 | 191 | 192 | 193 | 194 | 195 | 196 | #------------------------ 197 | def des_encript(input, ks, encrypt): 198 | # input - U32[] 199 | # output - U32[] 200 | # ks - des_key_shedule - U32[2][16] 201 | # encrypt - int 202 | # l, r, t, u - U32 203 | # i - int 204 | # s - U32[] 205 | 206 | l = input[0] 207 | r = input[1] 208 | t = U32(0) 209 | u = U32(0) 210 | 211 | r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0fL)) 212 | l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffffL)) 213 | r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333L)) 214 | l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ffL)) 215 | r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555L)) 216 | 217 | t = (r << 1)|(r >> 31) 218 | r = (l << 1)|(l >> 31) 219 | l = t 220 | 221 | s = ks # ??????????????? 222 | #print l, r 223 | if(encrypt): 224 | for i in range(0, 32, 4): 225 | rtup, u, t, s = D_ENCRYPT((l, r, i + 0), u, t, s) 226 | l = rtup[0] 227 | r = rtup[1] 228 | rtup, u, t, s = D_ENCRYPT((r, l, i + 2), u, t, s) 229 | r = rtup[0] 230 | l = rtup[1] 231 | else: 232 | for i in range(30, 0, -4): 233 | rtup, u, t, s = D_ENCRYPT((l, r, i - 0), u, t, s) 234 | l = rtup[0] 235 | r = rtup[1] 236 | rtup, u, t, s = D_ENCRYPT((r, l, i - 2), u, t, s) 237 | r = rtup[0] 238 | l = rtup[1] 239 | #print l, r 240 | l = (l >> 1)|(l << 31) 241 | r = (r >> 1)|(r << 31) 242 | 243 | r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555L)) 244 | l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ffL)) 245 | r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333L)) 246 | l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffffL)) 247 | r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0fL)) 248 | 249 | output = [l] 250 | output.append(r) 251 | l, r, t, u = U32(0), U32(0), U32(0), U32(0) 252 | return output 253 | 254 | def des_ecb_encrypt(input, ks, encrypt): 255 | # input - des_cblock - UChar[8] 256 | # output - des_cblock - UChar[8] 257 | # ks - des_key_shedule - U32[2][16] 258 | # encrypt - int 259 | 260 | #print input 261 | l0 = c2l(input[0:4]) 262 | l1 = c2l(input[4:8]) 263 | ll = [l0] 264 | ll.append(l1) 265 | #print ll 266 | ll = des_encript(ll, ks, encrypt) 267 | #print ll 268 | l0 = ll[0] 269 | l1 = ll[1] 270 | output = l2c(l0) 271 | output = output + l2c(l1) 272 | #print output 273 | l0, l1, ll[0], ll[1] = U32(0), U32(0), U32(0), U32(0) 274 | return output 275 | 276 | def des_set_key(key): 277 | # key - des_cblock - UChar[8] 278 | # schedule - des_key_schedule 279 | 280 | # register unsigned long c,d,t,s; 281 | # register unsigned char *in; 282 | # register unsigned long *k; 283 | # register int i; 284 | 285 | #k = schedule 286 | # in = key 287 | 288 | k = [] 289 | c = c2l(key[0:4]) 290 | d = c2l(key[4:8]) 291 | t = U32(0) 292 | 293 | d, c, t = PERM_OP((d, c, t), 4, U32(0x0f0f0f0fL)) 294 | c, t = HPERM_OP((c, t), -2, U32(0xcccc0000L)) 295 | d, t = HPERM_OP((d, t), -2, U32(0xcccc0000L)) 296 | d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555L)) 297 | c, d, t = PERM_OP((c, d, t), 8, U32(0x00ff00ffL)) 298 | d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555L)) 299 | 300 | d = (((d & U32(0x000000ffL)) << 16)|(d & U32(0x0000ff00L))|((d & U32(0x00ff0000L)) >> 16)|((c & U32(0xf0000000L)) >> 4)) 301 | c = c & U32(0x0fffffffL) 302 | 303 | for i in range(16): 304 | if (shifts2[i]): 305 | c = ((c >> 2)|(c << 26)) 306 | d = ((d >> 2)|(d << 26)) 307 | else: 308 | c = ((c >> 1)|(c << 27)) 309 | d = ((d >> 1)|(d << 27)) 310 | c = c & U32(0x0fffffffL) 311 | d = d & U32(0x0fffffffL) 312 | 313 | s= des_skb[0][int((c ) & U32(0x3f))]|\ 314 | des_skb[1][int(((c>> 6) & U32(0x03))|((c>> 7) & U32(0x3c)))]|\ 315 | des_skb[2][int(((c>>13) & U32(0x0f))|((c>>14) & U32(0x30)))]|\ 316 | des_skb[3][int(((c>>20) & U32(0x01))|((c>>21) & U32(0x06)) | ((c>>22) & U32(0x38)))] 317 | 318 | t= des_skb[4][int((d ) & U32(0x3f) )]|\ 319 | des_skb[5][int(((d>> 7) & U32(0x03))|((d>> 8) & U32(0x3c)))]|\ 320 | des_skb[6][int((d>>15) & U32(0x3f) )]|\ 321 | des_skb[7][int(((d>>21) & U32(0x0f))|((d>>22) & U32(0x30)))] 322 | #print s, t 323 | 324 | k.append(((t << 16)|(s & U32(0x0000ffffL))) & U32(0xffffffffL)) 325 | s = ((s >> 16)|(t & U32(0xffff0000L))) 326 | s = (s << 4)|(s >> 28) 327 | k.append(s & U32(0xffffffffL)) 328 | 329 | schedule = k 330 | 331 | return schedule 332 | -------------------------------------------------------------------------------- /server.cfg: -------------------------------------------------------------------------------- 1 | #======================================================================== 2 | [GENERAL] 3 | 4 | LISTEN_PORT:5865 5 | 6 | # If you want APS to authenticate you at WWW servers using NTLM then just leave this 7 | # value blank like PARENT_PROXY: and APS will connect to web servers directly. 8 | # You can specify more than one proxy by leaving a space between each one, and 9 | # APS will detect when one fails and automatically fail-over to the next. EG: 10 | #PARENT_PROXY:first_proxy second_proxy third_proxy 11 | # And NOTE that NTLM cannot pass through another proxy server. 12 | PARENT_PROXY:your_parentproxy 13 | 14 | PARENT_PROXY_PORT:8080 15 | 16 | # APS will poll the upstream proxy and attempt to fail-over to a new one if it doesn't 17 | # get a response within an appropriate time frame. The amount of time that it will 18 | # wait for a response before attempting fail-over is specified, in seconds, below: 19 | PARENT_PROXY_TIMEOUT:15 20 | 21 | # Set to 1 if you want to grant this authorization service to clients from other computers. 22 | # NOTE: all the users from other hosts that will be using you copy of APS for authentication 23 | # will be using your credentials in NTLM auth at the remote host. 24 | ALLOW_EXTERNAL_CLIENTS:0 25 | 26 | # If you want to allow some other but not all computers to use your proxy for authorization, 27 | # just set ALLOW_EXTERNAL_CLIENTS:0 and put friendly IP addresses here. 28 | # Use space as a delimiter. You may use hostnames as well as IP addresses if you wish. EG: 29 | #FRIENDLY_IPS: 192.168.1.1 friendlyhost.local otherhost 30 | # NOTE that special addesses don't work here (192.168.3.0 for example). 31 | FRIENDLY_IPS: 32 | 33 | # If you have some local intranet servers that require you to authenticate, but you 34 | # also want to be able to access the internet via an upstream proxy (ie, with the 35 | # PARENT_PROXY value above), then add the addresses that you use to access your intranet 36 | # servers here in the HOSTS_TO_BYPASS_PARENT_PROXY variable as a space delimitted list; 37 | # or otherwise, if you want to bypass the upstream proxy for *all possible hosts* (ie, every 38 | # one to whom a connection can be made directly without going through the proxy), change 39 | # the value of DIRECT_CONNECT_IF_POSSIBLE to 1. Note that use of one option does not 40 | # preclude use of the other also. Examples: 41 | #HOSTS_TO_BYPASS_PARENT_PROXY:myintranetserver.local 172.16.0.1 intranetbox 42 | # ... this would specify three hosts that ntlmaps will bypass the internet proxy for 43 | # and authenticate you to directly. 44 | #DIRECT_CONNECT_IF_POSSIBLE:1 45 | # ... all hosts to whom a connection can be made will now be authenticated to directly. 46 | HOSTS_TO_BYPASS_PARENT_PROXY: 47 | DIRECT_CONNECT_IF_POSSIBLE:0 48 | 49 | # Requested URLs are written to "url.log" file. May be useful. 50 | URL_LOG:0 51 | 52 | # When a network service listens for connections, there is a maximum number of connection 53 | # attempts to that service that the underlying OS will allow to backlog waiting for a response 54 | # before the OS will start dropping new connection attempts with 'Connection refused'. The 55 | # standard method of determining the maximum number of backlogged connections is to use the 56 | # SOMAXCONN constant, which is supposed to represent the maximum number that an OS will support 57 | # (for example, 5 on Windows 2000 Pro, and 200 on Windows 2000 server). However, because this 58 | # is a statically compiled value in a Python distribution, usually this instead represents the 59 | # the most conservative value (5 on all Windows platforms, and 128 on the GNU/Linux variant I 60 | # tried). So if you are running (for example) a massively threaded/parallel download manager, 61 | # the default value of, say, 5, or whatever SOMAXCONN happens to be set to, may be too low and 62 | # cause some connections to fail. The value below can be set to any integer (it seems that 63 | # Python just silently caps values above the hard limit for the underlying platform), or it can 64 | # be set to the special value of SOMAXCONN (i.e. MAX_CONNECTION_BACKLOG:SOMAXCONN), to use 65 | # whatever this value happens to be set to in your Python build. Setting this higher than 66 | # necessary may cause APS to consume more memory than you needed to. 67 | MAX_CONNECTION_BACKLOG:5 68 | 69 | #======================================================================== 70 | [CLIENT_HEADER] 71 | 72 | # This section describes what and how the server should change in the clients headers. 73 | # Made in order to prevent parent proxy from seeing that you are using wget instead of IE5.5 74 | 75 | Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, application/vnd.ms-powerpoint, */* 76 | 77 | # Various User-Agent strings for imitating various browsers and OSes. 78 | # Windows 98, IE 5.5: 79 | # User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98) 80 | # Windows 2000, IE 5.5: 81 | # User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT5) 82 | # Windows XP SP2, IE 6.0: 83 | User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) 84 | 85 | # You can uncomment these chages in client's header to mimic IE5+ better, but in this case 86 | # you may expirience problems with *.html if your client does not really handle compression. 87 | #Accept-Encoding: gzip, deflate 88 | 89 | #======================================================================== 90 | [DIGEST_AUTH] 91 | # What user's name to use during authorization. 92 | USER:username_to_use 93 | 94 | # Password. 95 | PASSWORD:your_digest_password 96 | 97 | 98 | #======================================================================== 99 | [NTLM_AUTH] 100 | 101 | # Optional value, if leaved blank then APS will use gethostname() to determine 102 | # host's name. 103 | # NOTE1: If you Linux host name differs from Windows host name then it may be that 104 | # MS server wont recognize you host at all and wont grant you access 105 | # to resources requested. Then you have to use this option and APS will use 106 | # this name in NTLM negotiations. 107 | # NOTE2: There are several reports that you can successfully use "foreign" host name 108 | # here. Say, if user may access a resource from 'host1' and may not from 'host2' 109 | # then there is a chance that APS running on 'host2' with NT_HOSTNAME:host1 will 110 | # be able to be granted access to the restricted resource. However use this on 111 | # you own risk as such a trick may be considered as a hack or something. 112 | NT_HOSTNAME: 113 | 114 | # Windows Domain. 115 | # NOTE: it is not full qualified internet domain, but windows network domain. 116 | NT_DOMAIN:your_domain 117 | 118 | # What user's name to use during authorization. It may differ form real current username. 119 | # If you enable NTLM_TO_BASIC, below, you can either leave this blank or simply 120 | # hash it out. 121 | USER:username_to_use 122 | 123 | # Password. Just leave it blank here and server will request it at the start time, 124 | # or, if you enable NTLM_TO_BASIC, below, you can either leave this blank or simply 125 | # hash it out, and you *won't* be prompted for a password at start time. 126 | PASSWORD:your_nt_password 127 | 128 | # Alternatively, fill in these hashes. You can use the hashes.py program supplied to 129 | # generate the hashes. 130 | # An example of the sort of thing to expect, for the password 'MyPassword' would be 131 | # dKyZykDe1CDcGnPmzqZ+xQAAAAAA 132 | # and 133 | # 8SxBgIPAXjp954WC5h9lLQAAAAAA 134 | # 135 | # If both of these settings are used, then the PASSWORD setting above is ignored. 136 | LM_HASHED_PW: 137 | NT_HASHED_PW: 138 | 139 | # If you are running ntlmaps on Windows and your password includes complex characters, 140 | # such as unicode characters outside of the ASCII 0-255 codepage for your native 141 | # language, and you are planning on inputting your password at the prompt when ntlmaps 142 | # starts, then you will need to enable COMPLEX_PASSWORD_INPUT, below. It should 143 | # be safe to leave enabled for everyone regardless, as ntlmaps will fall back to the 144 | # standard password input mechanism if for some reason it is unable to support complex 145 | # passwords, or if the extension is not required, such as for users on other platforms 146 | # like MacOS X or various *nixes. 147 | # Please note that because this mechanism relies on a custom C extension module 148 | # (win32console), that this will only work for people running ntlmaps with a supported 149 | # version of Python. Currently, that includes Python 1.5.2, and the current version 150 | # of Python available (2.4.1). 151 | COMPLEX_PASSWORD_INPUT:1 152 | 153 | # These two options replace old FULL_NTLM option. 154 | # NTLM authentication consists virtually of two parts: LM and NT. Windows95/98 use 155 | # only LM part, WindowsNT/2000 can use NT and LM or just NT part. 156 | # Almost always using just LM part will be enough. I had several reports 157 | # about LM and NT requirement and no about just NT. 158 | # So try to setup 1, 1 only if you have enough reasons to do so and when you understand 159 | # what you are doing. 160 | # 0, 0 is an illegal combination 161 | # NOTE: if you change these options then you have to setup flag option accordingly. 162 | LM_PART:1 163 | NT_PART:0 164 | 165 | # Highly experimental option. See research.txt for details. 166 | # LM - 06820000 167 | # NT - 05820000 168 | # LM + NT - 07820000 169 | NTLM_FLAGS: 06820000 170 | 171 | # This option makes APS try to translate NTLM authentication to very usual "Basic" 172 | # scheme. Almost all http clients know it. With this option set to 1 user will be requested 173 | # by his browser to enter his credentials and these username and password will be used by 174 | # APS for NTLM authentication at MS Proxy server or Web server. 175 | # In such a case different users can use one runnig APS with their own credentials. 176 | # NOTE1: currently translation works so it allows only one try for entering 177 | # username/password. If you make a mistake you will have to restart you browser. 178 | # NOTE2: With debug:1 basic username/password will be written in log file in clear 179 | # text format. I could try hide it, but the basic scheme is so weak that anybody 180 | # who had access to APS would be able to get it. 181 | NTLM_TO_BASIC:0 182 | 183 | #======================================================================== 184 | [DEBUG] 185 | 186 | # Set this to 1 if you want to see debug info in many log files. One per connection. 187 | DEBUG:0 188 | 189 | # Set this to 1 to get even more debug info. 190 | BIN_DEBUG:0 191 | 192 | # Set this to 1 to see some strange activity on screen. Actually you won't want it. 193 | SCR_DEBUG:0 194 | 195 | # Not actually a debug option but gives you some details on authentication process 196 | # into *.auth logs. Also see research.txt. 197 | AUTH_DEBUG:0 198 | 199 | # Set this to 1 if you want to see exceptions from threads (i.e. no threads are spawned) 200 | NOTHREADS:0 201 | 202 | -------------------------------------------------------------------------------- /doc/changelog.txt: -------------------------------------------------------------------------------- 1 | Version 0.9.9.8 Tuesday 21 October 2008 Matt Domsch 2 | - apply all patches submitted on the sourceforge site 3 | - apply some patches carried in Debian 4 | 5 | Version 0.9.9.0.1 Friday 27th January 2006 Darryl Dixon 6 | - Minor bugfix for Python 1.5.2 compatibility 7 | 8 | Version 0.9.9.7 Wednesday 20 July 2005 Darryl Dixon 9 | - Squash infinite poll loop failure in pipelined requests 10 | - Add support for complex passwords input at runtime on Windows hosts (via win32console module) 11 | - Fixup py1.5.2 compatability in can_connect() 12 | - Squash 100% CPU Bug 13 | 14 | Version 0.9.9.6 Tuesday 05 July 2005 Darryl Dixon 15 | - Squash bug introduced in 0.9.9.5 preventing mode change to www in proxy_client.py 16 | - Mark server.cfg as config file in ntlmaps.spec 17 | - Update copyright string 18 | 19 | Version 0.9.9.5 Thursday 16 June 2005 Darryl Dixon 20 | - Added socket config option for REUSEADDR in server.py 21 | - BUGFIX sourceforge ID#1221292 fix regression in CONNECT code that broke SSL 22 | 23 | Version 0.9.9.4 Friday 10th June 2005 Darryl Dixon 24 | - Minor fixups in ntlmaps.spec for better FHS and ntlmaps.nsi for Uninstaller correctness 25 | - PATCH sourceforge ID#1214559: Add patch for CONNECT support in Direct mode 26 | - PATCH sourceforge ID#1206709: Add patch for DIRECT_CONNECT_IF_POSSIBLE in proxy_client.py and config_affairs.py 27 | - Fix ntlmaps.nsi to reflect changed file locations 28 | - Fix setup.py to make server.cfg location work properly 29 | 30 | Version 0.9.9.3 Thursday 24th February 2005 Darryl Dixon 31 | - Fix self.monitor with no threadsToKill attribute in server.py 32 | - Trapped exception in config.py for bogus config file specified with -c 33 | - Moved all text files except COPYING into docs/ 34 | - Updated ntlmaps.spec for better FHS compliance 35 | 36 | Version 0.9.9.2 Wednesday 23rd February 2005 Darryl Dixon 37 | - Added support for hostnames to FRIENDLY_IPS in server.cfg and config_affairs.py 38 | - Include some preliminary (alpha) packaging stuff 39 | - REQUEST sourceforge ID#1143827: Changed logic in config.py to make # work in passwords 40 | - Add fix for Python 1.5.2 compatability to exception clause in config_affairs.py 41 | - Fix Host check in self.determine_mode() in proxy_client.py 42 | 43 | Version 0.9.9.1 Monday 14th February 2005 Darryl Dixon 44 | - Added HOSTS_TO_BYPASS_PARENT_PROXY configuration item in server.cfg 45 | - Added processing of HOSTS_TO_BYPASS_PARENT_PROXY to config_affairs.py 46 | - Moved www_client.py into proxy_client.py -- hide function names behind abstraction 47 | - Change program flow in server.py and proxy_client.py to support proxy bypass list 48 | - Cleaned up User-Agent stuff in server.cfg -- added WinXP SP2 IE6 option and made it the default 49 | 50 | Version 0.9.9 Thursday 10th February 2005 Darryl Dixon 51 | - Re-release 0.9.8.11 as 0.9.9 stable 52 | 53 | Version 0.9.8.11 Friday 14th January 2005 Darryl Dixon 54 | - Fixed use of split() function that broke Python 1.5.2 compatibility in config_affairs.py 55 | - Added traps for ValueError in proxy_client.py for Python 1.5.2 compatibility 56 | - Split testing of upstream proxy codepath in monitor_upstream.py into Python 2.x and 1.5.2 branches 57 | 58 | Version 0.9.8.10 Friday 24th December Darryl Dixon 59 | - Caught another exception condition for failed upstream proxy in monitor_upstream.py 60 | - Reworked program flow in monitor_upstream.py and server.py to fix 100% cpu bug introduced in 0.9.8.9 61 | 62 | Version 0.9.8.9 Thursday 2nd December Darryl Dixon 63 | - Change default values for PARENT_PROXY_PORT to 8080 and PARENT_PROXY_TIMEOUT to 15 in server.cfg 64 | - Squashed a bug in config_affairs.py that broke LISTEN_PORT when no PARENT_PROXY specified 65 | - Caught another exception condition in proxy_client.py 66 | - Clarified 'Could not create socket' error in server.py to reference port number 67 | - Added clause in monitor_upstream.py to prevent automatic failover in the case of single upstream proxy 68 | - Removed redundant 'pass' statement from server.py 69 | - Caught another exception condition for failed upstream proxy in monitor_upstream.py 70 | - BUGFIX sourceforge ID#1070604: Added exception handler for call to socket.gethostbyname_ex() in config_affairs.py 71 | - Moved copyright banner to appear earlier in initialisation 72 | - Added TODO to monitor_upstream.py 73 | - BUGFIX sourceforge ID#1070605: Added exception handler for call to open() in http_header.py 74 | 75 | Version 0.9.8.8 Tuesday 16th November 2004 Darryl Dixon 76 | - Squashed bug introduced in 0.9.8.7 with bad logger entry in proxy_client.py 77 | - Cleaned up handling of 'PASSWORD' config item in config_affairs.py to squash potential bug 78 | - Modified server.cfg to enhance description of 'PASSWORD' and 'USER' items 79 | - Slightly updated the readme.txt 80 | 81 | Version 0.9.8.7 Friday 12th November 2004 Darryl Dixon 82 | - BUGFIX sourceforge ID#798824: Modified exception trap for c_method = self.client_head_obj.get_http_method() in proxy_client.py 83 | - Replaced several TODOs from proxy_client.py with logger entries 84 | - Removed bogus 'accepting connections' comment from main.py 85 | - Squashed bug with SOMAXCONN setup trapping wrong exception 86 | - Moved some initialisation code from main.py to server.py 87 | - Clarified some code dealing with configuration items in main.py 88 | - Modified some of the startup text/order 89 | - Sanified processing of int() items in config_affairs.py 90 | - Removed redundant statement from NTLM section of config_affairs.py 91 | - Modified ntlm_auth.py to reflect NTLM_TO_BASIC as already an int 92 | - Properly corrected second instance of sourceforge ID#1061067 in ntlm_auth.py 93 | 94 | Version 0.9.8.6 Thursday 11th November 2004 Darryl Dixon 95 | - Added MAX_CONNECTION_BACKLOG configuration item 96 | - FEATURE/BUGFIX sourceforge request ID#831606: MAX_CONNECTION_BACKLOG now allows configurable listener 97 | - Clarified some historical entries in changelog 98 | - Minor cleanups of error text in config_affairs.py 99 | 100 | Version 0.9.8.5 Wednesday 10th November 2004 Darryl Dixon 101 | - Added PARENT_PROXY_TIMEOUT configuration item 102 | - Squashed a typo bug in config_affairs.py for HOST_IP_LIST 103 | - Updated Install.txt 104 | - BUGFIX sourceforge request ID#707725 and ID#1061067: Fixed 2 instances of error in ntlm_auth.py 105 | 106 | Version 0.9.8.4 Tuesday 9th November 2004 Darryl Dixon 107 | - Changed method of delivering signal to just call sigHandler() directly 108 | - Seriously reworked programme flow of monitor_upstream.py to accomodate change above 109 | - Added locking of critical sections to keep threads sane 110 | - Caught another exception in proxy_client.py 111 | 112 | Version 0.9.8.3 Sunday 7th November 2004 Darryl Dixon 113 | - Changed SIGALRM timer to new custom timerThread() (more portable) 114 | - Cleaned up signal handling to accomodate new timer implementation 115 | - Caught some more exceptions in proxy_client.py 116 | - Fixed corner case which b0rked timer 117 | 118 | Version 0.9.8.2 Friday 5th November 2004 Darryl Dixon 119 | - Added extra exception trapping for fault conditions in proxy_client.py 120 | - Added __init__.py to begin making ntlmaps relocatable 121 | - Changed from SIGUSR1 to SIGINT to make more portable 122 | - Removed two non-ascii comments causing warnings from compiler in md4.py and des_c.py 123 | 124 | Version 0.9.8.1 Monday 1st November 2004 Darryl Dixon 125 | - Added monitor_upstream.py for upstream mode that detects upstream proxy failure 126 | - Added ability to specify multiple upstream proxies for failover 127 | 128 | Verison 0.9.8 - Fri May 17 04:07:20 MSD 2002 129 | - internal redesign 130 | - config file redesign 131 | - fixed bug during HTTPS CONNECT authentication. 132 | - fixed bug with UNICODE string conversion in NTLM msg3 creation code. 133 | - no need in proxy port when proxy is not used 134 | - fixed minor bug with an exception that was eraised if there was no http header 135 | in server's response. 136 | - MSN Messenger and clients alike work again. It had been broken since APS 0.9.1 137 | - minor bug in header remake (Proxy Connection -> Connection) 138 | - fixed bug when client sends its header slowly and clients thread exits before 139 | doing anything useful. This was broken since version 0.9.7 140 | - new optional value in config file NT_HOSTNAME (see comment in server.cfg). 141 | - DOMAIN value in config is now NT_DOMAIN, to make it clear what domain name has 142 | to be used. 143 | - implemented NTLM to BASIC translation. (it gives only one try to enter credentials now) 144 | 145 | Verison 0.9.7 - Tue Dec 11 10:43:58 MSK 2001 146 | - added ability to do multiple authentications in persistent connection. It looks like that 147 | there is some kind of NTLM extension that requires client to authenticate himself to every 148 | new network location even during persistent connection. 149 | - fixed bug when request is sent to currently connected web server even if it is a request 150 | to different network location. 151 | - 'Negotiate' authentication method name for www-authentication has been changed to 'NTLM' 152 | for compatibility with IIS/4.0 153 | - added '-c' command line option to point to config file different than './server.cfg' 154 | - AUTH_DEBUG option added. It makes APS write detailed report on NTLM authentication 155 | process. 156 | - Highly experimental NTLM_FLAGS option allows changing NTLM flags in auth messages sent 157 | to remote server. 158 | 159 | Verison 0.9.5 - Tue Nov 6 03:02:53 MSK 2001 160 | - UNICODE string support during authorization (DOMAIN, USER, HOST). 161 | - NT response is calculated now. Actually UNICODE and NT response are part of NTLM, but 162 | most of times everything works without them. See FULL_NTLM option in server.cfg 163 | - some performance improvement (I hope). 164 | - now it can behave as a standalone proxy server and do NTLM authentication at Web servers as 165 | well. While NTLM cannot pass through proxy server this option may be of use in intranet 166 | environment with athorization at local webservices. Just leave PARENT_PROXY option blank and 167 | it will connect to remote servers itself. 168 | 169 | Verison 0.9.1 - Fri May 25 01:51:30 MSD 2001 170 | - if one side closed connection server tries sending data left in the buffer 171 | before closing connection to the other side. 172 | - new parameter in server.cfg - FRIENDLY_IPS that works 173 | in pair with ALLOW_EXTERNAL_CLIENTS:0. See server.cfg 174 | - a bit improved POST of long files, but not completely perfect. 175 | 176 | Verison 0.8.8 - Thu Apr 26 02:27:37 MSD 2001 177 | - calls of socket.bind() and socket.connect() are compatible with Python >=1.6 now 178 | - ALLOW_EXTERNAL_CLIENTS parameter in server.cfg to allow/disallow clients from external hosts 179 | - NT password to use may be entered at the start time from the console (see server.cfg) 180 | - calmed down debug info sent to console. Now it appears only if DEBUG is set to 1 in server.cfg 181 | 182 | Verison 0.8.6 - Tue Apr 17 02:31:18 MSD 2001 183 | - an ugly junkbuster's header delimiter '\012\012' worked around 184 | - hopefully fixed bug with leakage of MS Proxy info page into response with requested resource 185 | - value names are case insensitive now 186 | 187 | Verison 0.8.3 - Wed Apr 11 22:46:40 MSD 2001 188 | - POST and PUT should work with NTLM 189 | - possibly there is a bug in proxy response header extracting routine 190 | 191 | Verison 0.8.2 - Sun Apr 8 02:16:41 MSD 2001 192 | - Tunnel mode for HTTPS 'CONNECT' request 193 | - POST and PUT should work without authorization 194 | - fixed some bugs 195 | - POST does not work with NTLM authorization (working on this) 196 | 197 | Version 0.8.0 - Thu Mar 29 03:12:41 MSD 2001 198 | - First public release. 199 | 200 | -------------------------------------------------------------------------------- /ntlmaps/http_header.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import string, urlparse 21 | 22 | http_debug_file_name = 'http.debug' 23 | 24 | #----------------------------------------------------------------------- 25 | # tests client's header for correctness 26 | def test_client_http_header(header_str): 27 | request = string.split(header_str, '\012')[0] 28 | parts = string.split(request) 29 | 30 | # we have to have at least 3 words in the request 31 | # poor check 32 | if len(parts) < 3: 33 | return 0 34 | else: 35 | return 1 36 | 37 | 38 | #----------------------------------------------------------------------- 39 | # tests server's response header for correctness 40 | def test_server_http_header(header_str): 41 | response = string.split(header_str, '\012')[0] 42 | parts = string.split(response) 43 | 44 | # we have to have at least 2 words in the response 45 | # poor check 46 | if len(parts) < 2: 47 | return 0 48 | else: 49 | return 1 50 | 51 | #----------------------------------------------------------------------- 52 | def extract_http_header_str(buffer): 53 | # let's remove possible leading newlines 54 | t = string.lstrip(buffer) 55 | 56 | # searching for the RFC header's end 57 | delimiter = '\015\012\015\012' 58 | header_end = string.find(t, delimiter) 59 | 60 | if header_end < 0: 61 | # may be it is defective header made by junkbuster 62 | delimiter = '\012\012' 63 | header_end = string.find(t, delimiter) 64 | 65 | if header_end >=0: 66 | # we have found it, possibly 67 | ld = len(delimiter) 68 | header_str = t[0:header_end + ld] 69 | 70 | # Let's check if it is a proper header 71 | if test_server_http_header(header_str) or test_client_http_header(header_str): 72 | # if yes then let's do our work 73 | if (header_end + ld) >= len(t): 74 | rest_str = '' 75 | else: 76 | rest_str = t[header_end + ld:] 77 | else: 78 | # if not then let's leave the buffer as it is 79 | # NOTE: if there is some junk before right header we will never 80 | # find that header. Till timeout, I think. Not that good solution. 81 | header_str = '' 82 | rest_str = buffer 83 | 84 | else: 85 | # there is no complete header in the buffer 86 | header_str = '' 87 | rest_str = buffer 88 | 89 | return (header_str, rest_str) 90 | 91 | #----------------------------------------------------------------------- 92 | def extract_server_header(buffer): 93 | header_str, rest_str = extract_http_header_str(buffer) 94 | if header_str: 95 | header_obj = HTTP_SERVER_HEAD(header_str) 96 | else: 97 | header_obj = None 98 | 99 | return (header_obj, rest_str) 100 | 101 | #----------------------------------------------------------------------- 102 | def extract_client_header(buffer): 103 | header_str, rest_str = extract_http_header_str(buffer) 104 | if header_str: 105 | header_obj = HTTP_CLIENT_HEAD(header_str) 106 | else: 107 | header_obj = None 108 | 109 | return (header_obj, rest_str) 110 | 111 | #----------------------------------------------------------------------- 112 | def capitalize_value_name(str): 113 | tl = string.split(str, '-') 114 | for i in range(len(tl)): 115 | tl[i] = string.capitalize(tl[i]) 116 | 117 | return string.join(tl, '-') 118 | 119 | 120 | #----------------------------------------------------------------------- 121 | # some helper classes 122 | #----------------------------------------------------------------------- 123 | class HTTP_HEAD: 124 | pass 125 | 126 | #------------------------------- 127 | def __init__(self, head_str): 128 | self.head_source = '' 129 | self.params = None 130 | self.fields = None 131 | self.order_list = [] 132 | 133 | self.head_source = head_str 134 | head_str = string.strip(head_str) 135 | records = string.split(head_str, '\012') 136 | 137 | # Dealing with response line 138 | #fields = string.split(records[0], ' ', 2) 139 | t = string.split(string.strip(records[0])) 140 | fields = t[:2] + [string.join(t[2:])] 141 | 142 | self.fields = [] 143 | for i in fields: 144 | self.fields.append(string.strip(i)) 145 | 146 | # Dealing with params 147 | params = {} 148 | order_list = [] 149 | for i in records[1:]: 150 | parts = string.split(string.strip(i), ':', 1) 151 | pname = string.lower(string.strip(parts[0])) 152 | if not params.has_key(pname): 153 | params[pname] = [] 154 | order_list.append(string.lower(pname)) 155 | try: 156 | params[pname].append(string.strip(parts[1])) 157 | except: 158 | msg = "ERROR: Exception in head parsing. ValueName: '%s'" % pname 159 | #print msg 160 | self.debug(msg) 161 | 162 | self.params = params 163 | self.order_list = order_list 164 | 165 | 166 | #------------------------------- 167 | def debug(self, message): 168 | try: 169 | f = open(http_debug_file_name, 'a') 170 | f.write(message) 171 | f.write('\n=====\n') 172 | f.write(self.head_source) 173 | f.close() 174 | except IOError: 175 | pass 176 | # Yes, yes, I know, this is just sweeping it under the rug... 177 | # TODO: implement a persistent filehandle for logging debug messages to. 178 | 179 | #------------------------------- 180 | def copy(self): 181 | import copy 182 | return copy.deepcopy(self) 183 | 184 | 185 | #------------------------------- 186 | def get_param_values(self, param_name): 187 | param_name = string.lower(param_name) 188 | if self.params.has_key(param_name): 189 | return self.params[param_name] 190 | else: 191 | return [] 192 | 193 | #------------------------------- 194 | def del_param(self, param_name): 195 | param_name = string.lower(param_name) 196 | if self.params.has_key(param_name): del self.params[param_name] 197 | 198 | #------------------------------- 199 | def has_param(self, param_name): 200 | param_name = string.lower(param_name) 201 | return self.params.has_key(param_name) 202 | 203 | #------------------------------- 204 | def add_param_value(self, param_name, value): 205 | param_name = string.lower(param_name) 206 | if not self.params.has_key(param_name): 207 | self.params[param_name] = [] 208 | if param_name not in self.order_list: 209 | self.order_list.append(param_name) 210 | self.params[param_name].append(value) 211 | 212 | #------------------------------- 213 | def replace_param_value(self, param_name, value): 214 | self.del_param(param_name) 215 | self.add_param_value(param_name, value) 216 | 217 | #------------------------------- 218 | def __repr__(self, delimiter='\n'): 219 | res = '' 220 | cookies = '' 221 | res = string.join(self.fields, ' ') + '\n' 222 | 223 | for i in self.order_list: 224 | if self.params.has_key(i): 225 | if i == 'cookie': 226 | for k in self.params[i]: 227 | cookies = cookies + capitalize_value_name(i) + ': ' + k + '\n' 228 | else: 229 | for k in self.params[i]: 230 | res = res + capitalize_value_name(i) + ': ' + k + '\n' 231 | res = res + cookies 232 | res = res + '\n' 233 | 234 | return res 235 | 236 | #------------------------------- 237 | def send(self, socket): 238 | #""" 239 | res = '' 240 | cookies = '' 241 | res = string.join(self.fields, ' ') + '\015\012' 242 | 243 | for i in self.order_list: 244 | if self.params.has_key(i): 245 | if i == 'cookie': 246 | for k in self.params[i]: 247 | cookies = cookies + capitalize_value_name(i) + ': ' + k + '\015\012' 248 | else: 249 | for k in self.params[i]: 250 | res = res + capitalize_value_name(i) + ': ' + k + '\015\012' 251 | res = res + cookies 252 | res = res + '\015\012' 253 | #""" 254 | #res = self.__repr__('\015\012') 255 | # NOTE!!! 0.9.1 worked, 0.9.5 and 0.9.7 did not with MSN Messenger. 256 | # We had problem here that prevent MSN Messenger from working. 257 | # Some work is needed to make __rerp__ working instead of current code.. 258 | try: 259 | #socket.send(self.head_source) 260 | socket.send(res) 261 | # self.debug(res) 262 | return 1 263 | except: 264 | return 0 265 | 266 | #----------------------------------------------------------------------- 267 | class HTTP_SERVER_HEAD(HTTP_HEAD): 268 | 269 | #------------------------------- 270 | def get_http_version(self): 271 | return self.fields[0] 272 | 273 | #------------------------------- 274 | def get_http_code(self): 275 | return self.fields[1] 276 | 277 | #------------------------------- 278 | def get_http_message(self): 279 | return self.fields[2] 280 | 281 | #----------------------------------------------------------------------- 282 | class HTTP_CLIENT_HEAD(HTTP_HEAD): 283 | 284 | #------------------------------- 285 | def get_http_version(self): 286 | return self.fields[2] 287 | 288 | #------------------------------- 289 | def get_http_method(self): 290 | return self.fields[0] 291 | 292 | #------------------------------- 293 | def get_http_url(self): 294 | return self.fields[1] 295 | 296 | #------------------------------- 297 | def set_http_url(self, new_url): 298 | self.fields[1] = new_url 299 | 300 | #------------------------------- 301 | # There is some problem with www request header... 302 | # not all servers want to answer to requests with full url in request 303 | # but want have net location in 'Host' value and path in url. 304 | def make_right_header(self): 305 | if self.get_http_method() == 'CONNECT': 306 | net_location = self.get_http_url() 307 | else: 308 | url_tuple = urlparse.urlparse(self.get_http_url()) 309 | net_location = url_tuple[1] 310 | 311 | self.replace_param_value('Host', net_location) 312 | 313 | if self.get_http_method() != 'CONNECT': 314 | path = urlparse.urlunparse(tuple(['', ''] + list(url_tuple[2:]))) 315 | self.set_http_url(path) 316 | 317 | #------------------------------- 318 | def get_http_server(self): 319 | # trying to get host from url 320 | if self.get_http_method() == 'CONNECT': 321 | net_location = self.get_http_url() 322 | else: 323 | url_tuple = urlparse.urlparse(self.get_http_url()) 324 | net_location = url_tuple[1] 325 | 326 | # if there was no host in url then get it from 'Host' value 327 | if not net_location: 328 | net_location = self.get_param_values('Host')[0] 329 | 330 | if not net_location: 331 | net_location = 'localhost' 332 | 333 | # trying to parse user:passwd@www.some.domain:8080 334 | # is it needed? 335 | if '@' in net_location: 336 | cred, net_location = string.split(net_location, '@') 337 | if ':' in net_location: 338 | server, port = string.split(net_location, ':') 339 | port = int(port) 340 | else: 341 | server = net_location 342 | port = 80 343 | 344 | return server, port 345 | 346 | -------------------------------------------------------------------------------- /doc/ENCRYPTION.txt: -------------------------------------------------------------------------------- 1 | !== 2 | !== ENCRYPTION.txt for Samba release 2.2.0-alpha1 23 Nov 2000 3 | !== 4 | Contributor: Jeremy Allison 5 | Updated: April 19, 1999 6 | Note: Please refer to WinNT.txt also 7 | 8 | Subject: LanManager / Samba Password Encryption. 9 | ============================================================================ 10 | 11 | With the development of LanManager and Windows NT compatible password 12 | encryption for Samba, it is now able to validate user connections in 13 | exactly the same way as a LanManager or Windows NT server. 14 | 15 | This document describes how the SMB password encryption algorithm 16 | works and what issues there are in choosing whether you want to use 17 | it. You should read it carefully, especially the part about security 18 | and the "PROS and CONS" section. 19 | 20 | How does it work ? 21 | ------------------ 22 | 23 | LanManager encryption is somewhat similar to UNIX password 24 | encryption. The server uses a file containing a hashed value of a 25 | user's password. This is created by taking the user's plaintext 26 | password, capitalising it, and either truncating to 14 bytes (or 27 | padding to 14 bytes with null bytes). This 14 byte value is used as 28 | two 56 bit DES keys to encrypt a 'magic' eight byte value, forming a 29 | 16 byte value which is stored by the server and client. Let this value 30 | be known as the *hashed password*. 31 | 32 | Windows NT encryption is a higher quality mechanism, consisting 33 | of doing an MD4 hash on a Unicode version of the user's password. This 34 | also produces a 16 byte hash value that is non-reversible. 35 | 36 | When a client (LanManager, Windows for WorkGroups, Windows 95 or 37 | Windows NT) wishes to mount a Samba drive (or use a Samba resource) it 38 | first requests a connection and negotiates the protocol that the client 39 | and server will use. In the reply to this request the Samba server 40 | generates and appends an 8 byte, random value - this is stored in the 41 | Samba server after the reply is sent and is known as the *challenge*. 42 | 43 | The challenge is different for every client connection. 44 | 45 | The client then uses the hashed password (16 byte values described 46 | above), appended with 5 null bytes, as three 56 bit DES keys, each of 47 | which is used to encrypt the challenge 8 byte value, forming a 24 byte 48 | value known as the *response*. 49 | 50 | In the SMB call SMBsessionsetupX (when user level security is 51 | selected) or the call SMBtconX (when share level security is selected) 52 | the 24 byte response is returned by the client to the Samba server. 53 | For Windows NT protocol levels the above calculation is done on 54 | both hashes of the user's password and both responses are returned 55 | in the SMB call, giving two 24 byte values. 56 | 57 | The Samba server then reproduces the above calculation, using its own 58 | stored value of the 16 byte hashed password (read from the smbpasswd 59 | file - described later) and the challenge value that it kept from the 60 | negotiate protocol reply. It then checks to see if the 24 byte value it 61 | calculates matches the 24 byte value returned to it from the client. 62 | 63 | If these values match exactly, then the client knew the correct 64 | password (or the 16 byte hashed value - see security note below) and 65 | is thus allowed access. If not, then the client did not know the 66 | correct password and is denied access. 67 | 68 | Note that the Samba server never knows or stores the cleartext of the 69 | user's password - just the 16 byte hashed values derived from it. Also 70 | note that the cleartext password or 16 byte hashed values are never 71 | transmitted over the network - thus increasing security. 72 | 73 | IMPORTANT NOTE ABOUT SECURITY 74 | ----------------------------- 75 | 76 | The unix and SMB password encryption techniques seem similar on the 77 | surface. This similarity is, however, only skin deep. The unix scheme 78 | typically sends clear text passwords over the nextwork when logging 79 | in. This is bad. The SMB encryption scheme never sends the cleartext 80 | password over the network but it does store the 16 byte hashed values 81 | on disk. This is also bad. Why? Because the 16 byte hashed values are a 82 | "password equivalent". You cannot derive the user's password from them, 83 | but they could potentially be used in a modified client to gain access 84 | to a server. This would require considerable technical knowledge on 85 | behalf of the attacker but is perfectly possible. You should thus 86 | treat the smbpasswd file as though it contained the cleartext 87 | passwords of all your users. Its contents must be kept secret, and the 88 | file should be protected accordingly. 89 | 90 | Ideally we would like a password scheme which neither requires plain 91 | text passwords on the net or on disk. Unfortunately this is not 92 | available as Samba is stuck with being compatible with other SMB 93 | systems (WinNT, WfWg, Win95 etc). 94 | 95 | 96 | PROS AND CONS 97 | ------------- 98 | 99 | There are advantages and disadvantages to both schemes. 100 | 101 | Advantages of SMB Encryption: 102 | ----------------------------- 103 | 104 | - plain text passwords are not passed across the network. Someone using 105 | a network sniffer cannot just record passwords going to the SMB server. 106 | 107 | - WinNT doesn't like talking to a server that isn't using SMB 108 | encrypted passwords. It will refuse to browse the server if the server 109 | is also in user level security mode. It will insist on prompting the 110 | user for the password on each connection, which is very annoying. The 111 | only things you can do to stop this is to use SMB encryption. 112 | 113 | Advantages of non-encrypted passwords: 114 | -------------------------------------- 115 | 116 | - plain text passwords are not kept on disk. 117 | 118 | - uses same password file as other unix services such as login and 119 | ftp 120 | 121 | - you are probably already using other services (such as telnet and 122 | ftp) which send plain text passwords over the net, so not sending them 123 | for SMB isn't such a big deal. 124 | 125 | Note that Windows NT 4.0 Service pack 3 changed the default for 126 | permissible authentication so that plaintext passwords are *never* 127 | sent over the wire. The solution to this is either to switch to 128 | encrypted passwords with Samba or edit the Windows NT registry to 129 | re-enable plaintext passwords. See the document WinNT.txt for 130 | details on how to do this. 131 | 132 | The smbpasswd file. 133 | ------------------- 134 | 135 | In order for Samba to participate in the above protocol it must 136 | be able to look up the 16 byte hashed values given a user name. 137 | Unfortunately, as the UNIX password value is also a one way hash 138 | function (ie. it is impossible to retrieve the cleartext of the user's 139 | password given the UNIX hash of it) then a separate password file 140 | containing this 16 byte value must be kept. To minimise problems with 141 | these two password files, getting out of sync, the UNIX /etc/passwd and 142 | the smbpasswd file, a utility, mksmbpasswd.sh, is provided to generate 143 | a smbpasswd file from a UNIX /etc/passwd file. 144 | 145 | To generate the smbpasswd file from your /etc/passwd file use the 146 | following command :- 147 | 148 | cat /etc/passwd | mksmbpasswd.sh >/usr/local/samba/private/smbpasswd 149 | 150 | If you are running on a system that uses NIS, use 151 | 152 | ypcat passwd | mksmbpasswd.sh >/usr/local/samba/private/smbpasswd 153 | 154 | The mksmbpasswd.sh program is found in the Samba source directory. By 155 | default, the smbpasswd file is stored in :- 156 | 157 | /usr/local/samba/private/smbpasswd 158 | 159 | The owner of the /usr/local/samba/private directory should be set to 160 | root, and the permissions on it should be set to :- 161 | 162 | r-x------ 163 | 164 | The command 165 | 166 | chmod 500 /usr/local/samba/private 167 | 168 | will do the trick. Likewise, the smbpasswd file inside the private 169 | directory should be owned by root and the permissions on is should be 170 | set to 171 | 172 | rw------- 173 | 174 | by the command :- 175 | 176 | chmod 600 smbpasswd. 177 | 178 | The format of the smbpasswd file is 179 | 180 | username:uid:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[Account type]:LCT-:Long name 181 | 182 | Although only the username, uid, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX, 183 | [Account type] and last-change-time sections are significant and 184 | are looked at in the Samba code. 185 | 186 | It is *VITALLY* important that there by 32 'X' characters between the 187 | two ':' characters in the XXX sections - the smbpasswd and Samba code 188 | will fail to validate any entries that do not have 32 characters 189 | between ':' characters. The first XXX section is for the Lanman password 190 | hash, the second is for the Windows NT version. 191 | 192 | When the password file is created all users have password entries 193 | consisting of 32 'X' characters. By default this disallows any access 194 | as this user. When a user has a password set, the 'X' characters change 195 | to 32 ascii hexadecimal digits (0-9, A-F). These are an ascii 196 | representation of the 16 byte hashed value of a user's password. 197 | 198 | To set a user to have no password (not recommended), edit the file 199 | using vi, and replace the first 11 characters with the asci text 200 | 201 | NO PASSWORD 202 | 203 | Eg. To clear the password for user bob, his smbpasswd file entry would 204 | look like : 205 | 206 | bob:100:NO PASSWORDXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U ]:LCT-00000000:Bob's full name:/bobhome:/bobshell 207 | 208 | If you are allowing users to use the smbpasswd command to set their own 209 | passwords, you may want to give users NO PASSWORD initially so they do 210 | not have to enter a previous password when changing to their new 211 | password (not recommended). In order for you to allow this the 212 | smbpasswd program must be able to connect to the smbd daemon as 213 | that user with no password. Enable this by adding the line : 214 | 215 | null passwords = true 216 | 217 | to the [global] section of the smb.conf file (this is why the 218 | above scenario is not recommended). Preferably, allocate your 219 | users a default password to begin with, so you do not have 220 | to enable this on your server. 221 | 222 | Note : This file should be protected very carefully. Anyone with 223 | access to this file can (with enough knowledge of the protocols) gain 224 | access to your SMB server. The file is thus more sensitive than a 225 | normal unix /etc/passwd file. 226 | 227 | The smbpasswd Command. 228 | ---------------------- 229 | 230 | The smbpasswd command maintains the two 32 byte password fields in 231 | the smbpasswd file. If you wish to make it similar to the unix passwd 232 | or yppasswd programs, install it in /usr/local/samba/bin (or your main 233 | Samba binary directory). 234 | 235 | Note that as of Samba 1.9.18p4 this program MUST NOT BE INSTALLED 236 | setuid root (the new smbpasswd code enforces this restriction so 237 | it cannot be run this way by accident). 238 | 239 | smbpasswd now works in a client-server mode where it contacts 240 | the local smbd to change the user's password on its behalf. This 241 | has enormous benefits - as follows. 242 | 243 | 1). smbpasswd no longer has to be setuid root - an enormous 244 | range of potential security problems is eliminated. 245 | 246 | 2). smbpasswd now has the capability to change passwords 247 | on Windows NT servers (this only works when the request is 248 | sent to the NT Primary Domain Controller if you are changing 249 | an NT Domain user's password). 250 | 251 | To run smbpasswd as a normal user just type : 252 | 253 | smbpasswd 254 | Old SMB password: 255 | New SMB Password: < type new value > 256 | Repeat New SMB Password: < re-type new value > 257 | 258 | If the old value does not match the current value stored for that user, 259 | or the two new values do not match each other, then the password will 260 | not be changed. 261 | 262 | If invoked by an ordinary user it will only allow the user to change 263 | his or her own Samba password. 264 | 265 | If run by the root user smbpasswd may take an optional argument, 266 | specifying the user name whose SMB password you wish to change. Note 267 | that when run as root smbpasswd does not prompt for or check the old 268 | password value, thus allowing root to set passwords for users who have 269 | forgotten their passwords. 270 | 271 | smbpasswd is designed to work in the same way and be familiar to UNIX 272 | users who use the passwd or yppasswd commands. 273 | 274 | For more details on using smbpasswd refer to the man page which 275 | will always be the definitive reference. 276 | 277 | Setting up Samba to support LanManager Encryption. 278 | -------------------------------------------------- 279 | 280 | This is a very brief description on how to setup samba to support 281 | password encryption. More complete instructions will probably be added 282 | later. 283 | 284 | 1) compile and install samba as usual 285 | 286 | 2) if your system can't compile the module getsmbpass.c then remove the 287 | -DSMBGETPASS define from the Makefile. 288 | 289 | 3) enable encrypted passwords in smb.conf by adding the line 290 | "encrypt passwords = yes" in the [global] section 291 | 292 | 4) create the initial smbpasswd password file in the place you 293 | specified in the Makefile. A simple way to do this based on your 294 | existing Makefile (assuming it is in a reasonably standard format) is 295 | like this: 296 | 297 | cat /etc/passwd | mksmbpasswd.sh > /usr/local/samba/private/smbpasswd 298 | 299 | Change ownership of private and smbpasswd to root. 300 | 301 | chown -R root /usr/local/samba/private 302 | 303 | Set the correct permissions on /usr/local/samba/private 304 | 305 | chmod 500 /usr/local/samba/private 306 | 307 | Set the correct permissions on /usr/local/samba/private/smbpasswd 308 | 309 | chmod 600 /usr/local/samba/private/smbpasswd 310 | 311 | note that the mksmbpasswd.sh script is in the samba source directory. 312 | 313 | If this fails then you will find that you will need entries that look 314 | like this: 315 | 316 | # SMB password file. 317 | tridge:148:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U ]:LCT-00000000:Andrew Tridgell:/home/tridge:/bin/tcsh 318 | 319 | note that the uid and username fields must be right. Also, you must get 320 | the number of X's right (there should be 32). 321 | 322 | 5) set the passwords for users using the smbpasswd command. For 323 | example, as root you could do "smbpasswd tridge" 324 | 325 | 6) try it out! 326 | 327 | Note that you can test things using smbclient, as it also now supports 328 | encryption. 329 | 330 | ============================================================================== 331 | Footnote: Please refer to WinNT.txt also 332 | 333 | 334 | -------------------------------------------------------------------------------- /ntlmaps/ntlm_messages.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import ntlm_procs, utils 21 | import base64, string 22 | 23 | #--------------------------------------------------------------------- 24 | class record: 25 | 26 | def __init__(self, data, offset=0): 27 | "" 28 | self.data = data 29 | self.len = len(data) 30 | self.offset = 0 31 | self.next_offset = self.offset + self.len 32 | 33 | #--------------------------------------------------------------------- 34 | # helper function for creation info field in message 3 35 | def create_record_info(self, offset): 36 | "" 37 | self.offset = offset 38 | len1 = utils.int2chrs(self.len) 39 | len2 = len1 40 | data_off = utils.int2chrs(self.offset) 41 | self.record_info = len1 + len2 + data_off + '\000\000' 42 | 43 | # looks like the length is always = 8 bytes 44 | self.next_offset = offset + self.len 45 | 46 | #--------------------------------------------------------------------- 47 | def create_message1(environment_dict): 48 | "" 49 | ed = environment_dict 50 | # overall lenght = 48 bytes 51 | protocol = 'NTLMSSP\000' #name 52 | type = '\001\000' #type 1 53 | zeros1 = '\000\000' 54 | 55 | flags = utils.hex2str(ed['FLAGS']) 56 | 57 | zeros2 = '\000\000\000\000\000\000\000\000\000' 58 | zeros3 = '\000\000\000\000\000\000\000\000\000\000\000' 59 | smthg1 = '0\000\000\000\000\000\000\000' # something with chr(48) length? 60 | smthg2 = '0\000\000\000' # something with chr(48) lenght? 61 | 62 | msg1 = protocol + type + zeros1 + flags + zeros2 + zeros3 + smthg1 + smthg2 63 | msg1 = base64.encodestring(msg1) 64 | msg1 = string.replace(msg1, '\012', '') 65 | 66 | return msg1 67 | 68 | #--------------------------------------------------------------------- 69 | def create_message3(nonce, environment_dict): 70 | "" 71 | ed = environment_dict 72 | 73 | flags = utils.hex2str(ed['FLAGS']) 74 | 75 | protocol = 'NTLMSSP\000' #name 76 | type = '\003\000' #type 3 77 | head = protocol + type + '\000\000' 78 | 79 | domain_rec = record(ed['DOMAIN']) 80 | user_rec = record(ed['USER']) 81 | host_rec = record(ed['HOST']) 82 | 83 | additional_rec = record('') 84 | 85 | if ed['LM']: 86 | lm_rec = record(ntlm_procs.calc_resp(ed['LM_HASHED_PW'], nonce)) 87 | else: 88 | lm_rec = record('') 89 | 90 | if ed['NT']: 91 | nt_rec = record(ntlm_procs.calc_resp(ed['NT_HASHED_PW'], nonce)) 92 | else: 93 | nt_rec = record('') 94 | 95 | # length of the head and five infos for LM, NT, Domain, User, Host 96 | domain_offset = len(head) + 5 * 8 97 | 98 | # and unknown record info and flags' lenght 99 | if ed['NTLM_MODE'] == 0: 100 | domain_offset = domain_offset + 8 + len(flags) 101 | 102 | # create info fields 103 | domain_rec.create_record_info(domain_offset) 104 | user_rec.create_record_info(domain_rec.next_offset) 105 | host_rec.create_record_info(user_rec.next_offset) 106 | lm_rec.create_record_info(host_rec.next_offset) 107 | nt_rec.create_record_info(lm_rec.next_offset) 108 | additional_rec.create_record_info(nt_rec.next_offset) 109 | 110 | # data part of the message 3 111 | data_part = domain_rec.data + user_rec.data + host_rec.data + lm_rec.data + nt_rec.data 112 | 113 | # build message 3 114 | m3 = head + lm_rec.record_info + nt_rec.record_info + domain_rec.record_info + \ 115 | user_rec.record_info + host_rec.record_info 116 | 117 | # Experimental feature !!! 118 | if ed['NTLM_MODE'] == 0: 119 | m3 = m3 + additional_rec.record_info + flags 120 | 121 | m3 = m3 + data_part 122 | 123 | # Experimental feature !!! 124 | if ed['NTLM_MODE'] == 0: 125 | m3 = m3 + additional_rec.data 126 | 127 | # base64 encode 128 | m3 = base64.encodestring(m3) 129 | m3 = string.replace(m3, '\012', '') 130 | 131 | return m3 132 | 133 | #--------------------------------------------------------------------- 134 | def parse_message2(msg2): 135 | "" 136 | msg2 = base64.decodestring(msg2) 137 | # protocol = msg2[0:7] 138 | # msg_type = msg2[7:9] 139 | nonce = msg2[24:32] 140 | 141 | return nonce 142 | 143 | #--------------------------------------------------------------------- 144 | def item(item_str): 145 | "" 146 | item = {} 147 | res = '' 148 | item['len1'] = utils.bytes2int(item_str[0:2]) 149 | item['len2'] = utils.bytes2int(item_str[2:4]) 150 | item['offset'] = utils.bytes2int(item_str[4:6]) 151 | 152 | res = res + '%s\n\nlength (two times), offset, delimiter\n' % (utils.str2hex(item_str)) 153 | 154 | res = res + '%s decimal: %3d # length 1\n' % (utils.int2hex_str(item['len1']), item['len1']) 155 | res = res + '%s decimal: %3d # length 2\n' % (utils.int2hex_str(item['len2']), item['len2']) 156 | res = res + '%s decimal: %3d # offset\n' % (utils.int2hex_str(item['offset']), item['offset']) 157 | res = res + '%s # delimiter (two zeros)\n\n' % utils.str2hex(item_str[-2:]) 158 | item['string'] = res 159 | 160 | return item 161 | 162 | #--------------------------------------------------------------------- 163 | def flags(flag_str): 164 | "" 165 | res = '' 166 | res = res + '%s\n\n' % utils.str2hex(flag_str) 167 | flags = utils.bytes2int(flag_str[0:2]) 168 | res = res + '%s # flags\n' % (utils.int2hex_str(flags)) 169 | res = res + 'Binary:\nlayout 87654321 87654321\n' 170 | res = res + ' %s %s\n' % (utils.byte2bin_str(flag_str[1]), utils.byte2bin_str(flag_str[0])) 171 | 172 | flags2 = utils.bytes2int(flag_str[2:4]) 173 | res = res + '%s # more flags ???\n' % (utils.int2hex_str(flags2)) 174 | res = res + 'Binary:\nlayout 87654321 87654321\n' 175 | res = res + ' %s %s\n' % (utils.byte2bin_str(flag_str[3]), utils.byte2bin_str(flag_str[2])) 176 | 177 | #res = res + '%s # delimiter ???\n' % m_hex[(cur + 2) * 2: (cur + 4) * 2] 178 | 179 | return res 180 | 181 | #--------------------------------------------------------------------- 182 | def unknown_part(bin_str): 183 | "" 184 | res = '' 185 | res = res + 'Hex : %s\n' % utils.str2hex(bin_str, ' ') 186 | res = res + 'String : %s\n' % utils.str2prn_str(bin_str, ' ') 187 | res = res + 'Decimal: %s\n' % utils.str2dec(bin_str, ' ') 188 | 189 | return res 190 | 191 | #--------------------------------------------------------------------- 192 | def debug_message1(msg): 193 | "" 194 | m_ = base64.decodestring(msg) 195 | m_hex = utils.str2hex(m_) 196 | 197 | res = '' 198 | res = res + '==============================================================\n' 199 | res = res + 'NTLM Message 1 report:\n' 200 | res = res + '---------------------------------\n' 201 | res = res + 'Base64: %s\n' % msg 202 | res = res + 'String: %s\n' % utils.str2prn_str(m_) 203 | res = res + 'Hex: %s\n' % m_hex 204 | cur = 0 205 | 206 | res = res + '---------------------------------\n' 207 | cur_len = 12 208 | res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24]) 209 | res = res + '%s\nmethod name 0/8\n%s # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8])) 210 | res = res + '0x%s%s # message type\n' % (m_hex[18:20], m_hex[16:18]) 211 | res = res + '%s # delimiter (zeros)\n' % m_hex[20:24] 212 | cur = cur + cur_len 213 | 214 | res = res + '---------------------------------\n' 215 | cur_len = 4 216 | res = res + 'Flags %d/%d\n' % (cur, cur_len) 217 | res = res + flags(m_[cur: cur + cur_len]) 218 | cur = cur + cur_len 219 | 220 | res = res + '---------------------------------\n' 221 | cur_len = len(m_) - cur 222 | res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len) 223 | res = res + unknown_part(m_[cur: cur + cur_len]) 224 | 225 | res = res + '\nEnd of message 1 report.\n' 226 | 227 | return res 228 | 229 | #--------------------------------------------------------------------- 230 | def debug_message2(msg): 231 | "" 232 | m_ = base64.decodestring(msg) 233 | m_hex = utils.str2hex(m_) 234 | res = '' 235 | res = res + '==============================================================\n' 236 | res = res + 'NTLM Message 2 report:\n' 237 | res = res + '---------------------------------\n' 238 | res = res + 'Base64: %s\n' % msg 239 | res = res + 'String: %s\n' % utils.str2prn_str(m_) 240 | res = res + 'Hex: %s\n' % m_hex 241 | cur = 0 242 | 243 | res = res + '---------------------------------\n' 244 | cur_len = 12 245 | res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24]) 246 | res = res + '%s\nmethod name 0/8\n%s # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8])) 247 | res = res + '0x%s%s # message type\n' % (m_hex[18:20], m_hex[16:18]) 248 | res = res + '%s # delimiter (zeros)\n' % m_hex[20:24] 249 | cur = cur + cur_len 250 | 251 | res = res + '---------------------------------\n' 252 | cur_len = 8 253 | res = res + 'Lengths and Positions %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) 254 | 255 | cur_len = 8 256 | res = res + 'Domain ??? %d/%d\n' % (cur, cur_len) 257 | dom = item(m_[cur:cur+cur_len]) 258 | res = res + dom['string'] 259 | cur = cur + cur_len 260 | 261 | res = res + '---------------------------------\n' 262 | cur_len = 4 263 | res = res + 'Flags %d/%d\n' % (cur, cur_len) 264 | res = res + flags(m_[cur: cur + cur_len]) 265 | cur = cur + cur_len 266 | 267 | res = res + '---------------------------------\n' 268 | cur_len = 8 269 | res = res + 'NONCE %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) 270 | cur = cur + cur_len 271 | 272 | res = res + '---------------------------------\n' 273 | cur_len = dom['offset'] - cur 274 | res = res + 'Unknown data %d/%d:\n' % (cur, cur_len) 275 | res = res + unknown_part(m_[cur: cur + cur_len]) 276 | cur = cur + cur_len 277 | 278 | res = res + '---------------------------------\n' 279 | cur_len = dom['len1'] 280 | res = res + 'Domain ??? %d/%d:\n' % (cur, cur_len) 281 | res = res + 'Hex: %s\n' % m_hex[cur * 2: (cur + cur_len) * 2] 282 | res = res + 'String: %s\n\n' % utils.str2prn_str(m_[cur : cur + cur_len]) 283 | cur = cur + cur_len 284 | 285 | res = res + '---------------------------------\n' 286 | cur_len = len(m_) - cur 287 | res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len) 288 | res = res + unknown_part(m_[cur: cur + cur_len]) 289 | 290 | res = res + '\nEnd of message 2 report.\n' 291 | 292 | return res 293 | 294 | #--------------------------------------------------------------------- 295 | def debug_message3(msg): 296 | "" 297 | m_ = base64.decodestring(msg) 298 | m_hex = utils.str2hex(m_) 299 | 300 | res = '' 301 | res = res + '==============================================================\n' 302 | res = res + 'NTLM Message 3 report:\n' 303 | res = res + '---------------------------------\n' 304 | res = res + 'Base64: %s\n' % msg 305 | res = res + 'String: %s\n' % utils.str2prn_str(m_) 306 | res = res + 'Hex: %s\n' % m_hex 307 | cur = 0 308 | 309 | res = res + '---------------------------------\n' 310 | cur_len = 12 311 | res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24]) 312 | res = res + '%s\nmethod name 0/8\n%s # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8])) 313 | res = res + '0x%s%s # message type\n' % (m_hex[18:20], m_hex[16:18]) 314 | res = res + '%s # delimiter (zeros)\n' % m_hex[20:24] 315 | cur = cur + cur_len 316 | 317 | res = res + '---------------------------------\n' 318 | cur_len = 48 319 | res = res + 'Lengths and Positions %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) 320 | 321 | cur_len = 8 322 | res = res + 'LAN Manager response %d/%d\n' % (cur, cur_len) 323 | lmr = item(m_[cur:cur+cur_len]) 324 | res = res + lmr['string'] 325 | cur = cur + cur_len 326 | 327 | cur_len = 8 328 | res = res + 'NT response %d/%d\n' % (cur, cur_len) 329 | ntr = item(m_[cur:cur+cur_len]) 330 | res = res + ntr['string'] 331 | cur = cur + cur_len 332 | 333 | cur_len = 8 334 | res = res + 'Domain string %d/%d\n' % (cur, cur_len) 335 | dom = item(m_[cur:cur+cur_len]) 336 | res = res + dom['string'] 337 | cur = cur + cur_len 338 | 339 | cur_len = 8 340 | res = res + 'User string %d/%d\n' % (cur, cur_len) 341 | user = item(m_[cur:cur+cur_len]) 342 | res = res + user['string'] 343 | cur = cur + cur_len 344 | 345 | cur_len = 8 346 | res = res + 'Host string %d/%d\n' % (cur, cur_len) 347 | host = item(m_[cur:cur+cur_len]) 348 | res = res + host['string'] 349 | cur = cur + cur_len 350 | 351 | cur_len = 8 352 | res = res + 'Unknow item record %d/%d\n' % (cur, cur_len) 353 | unknown = item(m_[cur:cur+cur_len]) 354 | res = res + unknown['string'] 355 | cur = cur + cur_len 356 | 357 | res = res + '---------------------------------\n' 358 | cur_len = 4 359 | res = res + 'Flags %d/%d\n' % (cur, cur_len) 360 | res = res + flags(m_[cur: cur + cur_len]) 361 | cur = cur + cur_len 362 | 363 | res = res + '---------------------------------\n' 364 | cur_len = dom['len1'] + user['len1'] + host['len1'] 365 | res = res + 'Domain, User, Host strings %d/%d\n%s\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2], utils.str2prn_str(m_[cur:cur + cur_len])) 366 | 367 | cur_len = dom['len1'] 368 | res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2] 369 | res = res + 'Domain name %d/%d:\n' % (cur, cur_len) 370 | res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)])) 371 | cur = cur + cur_len 372 | 373 | cur_len = user['len1'] 374 | res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2] 375 | res = res + 'User name %d/%d:\n' % (cur, cur_len) 376 | res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)])) 377 | cur = cur + cur_len 378 | 379 | cur_len = host['len1'] 380 | res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2] 381 | res = res + 'Host name %d/%d:\n' % (cur, cur_len) 382 | res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)])) 383 | cur = cur + cur_len 384 | 385 | res = res + '---------------------------------\n' 386 | cur_len = lmr['len1'] 387 | res = res + 'LAN Manager response %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) 388 | cur = cur + cur_len 389 | 390 | res = res + '---------------------------------\n' 391 | cur_len = ntr['len1'] 392 | res = res + 'NT response %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) 393 | cur = cur + cur_len 394 | 395 | res = res + '---------------------------------\n' 396 | cur_len = len(m_) - cur 397 | res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len) 398 | res = res + unknown_part(m_[cur: cur + cur_len]) 399 | 400 | res = res + '\nEnd of message 3 report.\n' 401 | return res 402 | 403 | -------------------------------------------------------------------------------- /ntlmaps/ntlm_auth.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | import string, select, base64 21 | import ntlm_messages, utils, ntlm_procs 22 | 23 | class ntlm_auther: 24 | """ 25 | NTLM authenticator class. Makes an HTTP authentication using NTLM method. 26 | """ 27 | 28 | #----------------------------------------------------------------------- 29 | def __init__(self): 30 | "" 31 | pass 32 | 33 | #----------------------------------------------------------------------- 34 | def proxy_ntlm_authentication(self, connection): 35 | "" 36 | connection.logger.log('*** Authorization in progress...\n') 37 | 38 | connection.close_rserver() 39 | 40 | # build an environment 41 | env = self.build_env_dict(connection) 42 | 43 | if env['NTLM_TO_BASIC']: 44 | got_credentials = self.translate_to_basic(env, connection, '407') 45 | 46 | if not got_credentials: 47 | connection.logger.log("*** Passing modified server's response to clent.\n") 48 | connection.logger.log('*** End of firts stage of NTLM translation.\n') 49 | return 50 | 51 | connection.connect_rserver() 52 | 53 | NTLM_msg1 = ntlm_messages.create_message1(env) 54 | connection.logger_auth.log(ntlm_messages.debug_message1(NTLM_msg1)) 55 | 56 | tmp_client_head_obj = connection.client_head_obj.copy() 57 | tmp_client_head_obj.replace_param_value('Proxy-Connection', 'Keep-Alive') 58 | tmp_client_head_obj.replace_param_value('Proxy-Authorization', 'NTLM ' + NTLM_msg1) 59 | 60 | connection.reset_rserver() 61 | connection.rserver_buffer = '' 62 | connection.logger.log('*** Remote server buffer flushed.\n') 63 | 64 | # If we are POST/PUT-ing a large chunk of data we don't want 65 | # to do this at this time, so we change the data to 'abc' with 66 | # lenght = 3. 67 | if connection.client_head_obj.has_param('Content-Length'): 68 | tmp_client_head_obj.replace_param_value('Content-Length', '3') 69 | 70 | connection.logger.log('*** Fake NTLM header with Msg1:\n=====\n' + tmp_client_head_obj.__repr__()) 71 | connection.logger.log('*** Sending Fake NTLM header with Msg1...') 72 | tmp_client_head_obj.send(connection.rserver_socket) 73 | connection.logger.log('Done.\n') 74 | 75 | if connection.client_head_obj.has_param('Content-Length'): 76 | 77 | try: 78 | connection.logger.log("*** Sending fake 'abc' bytes body...") 79 | connection.rserver_socket.send('abc') 80 | connection.logger.log("Done.\n") 81 | except: 82 | # could not send data to remote server and have to end function 83 | connection.rserver_socket_closed = 1 84 | connection.logger.log('Failed.\n*** Could not send client data to remote server. Exception in send().\n') 85 | return 86 | else: 87 | connection.logger.log("*** There must be no body to send.\n") 88 | 89 | 90 | connection.logger.log('*** Waiting for message 2 from remote server...\n') 91 | while((not connection.rserver_all_got) and (not connection.rserver_socket_closed)): 92 | select.select([connection.rserver_socket.fileno()], [], [], 2.0) 93 | connection.run_rserver_loop() 94 | if connection.config['DEBUG']['SCR_DEBUG']: print '\b+', 95 | 96 | if connection.rserver_head_obj: 97 | connection.logger.log('*** Got NTLM message 2 from remote server.\n') 98 | else: 99 | # could not get response with msg2 from remote server and have to end function 100 | connection.logger.log('*** Could not get response with msg2 from remote server.\n') 101 | connection.logger.log('*** Stop Request = %d.\n' % connection.stop_request) 102 | return 103 | 104 | auth = connection.rserver_head_obj.get_param_values('Proxy-Authenticate') 105 | if auth: 106 | msg2 = string.strip(string.split(auth[0])[1]) 107 | connection.logger_auth.log(ntlm_messages.debug_message2(msg2)) 108 | nonce = ntlm_messages.parse_message2(msg2) 109 | NTLM_msg3 = ntlm_messages.create_message3(nonce, env) 110 | connection.logger_auth.log(ntlm_messages.debug_message3(NTLM_msg3)) 111 | else: 112 | NTLM_msg3 = '' 113 | 114 | tmp_client_head_obj = connection.client_head_obj.copy() 115 | tmp_client_head_obj.replace_param_value('Proxy-Authorization', 'NTLM ' + NTLM_msg3) 116 | 117 | connection.reset_rserver() 118 | connection.rserver_buffer = '' 119 | connection.logger.log('*** Remote server buffer flushed.\n') 120 | connection.logger.log('*** Sending Fake NTLM header (not body) with Msg3...') 121 | tmp_client_head_obj.send(connection.rserver_socket) 122 | connection.logger.log('Done.\n') 123 | connection.logger.log('*** Fake NTLM header with Msg3:\n=====\n' + tmp_client_head_obj.__repr__()) 124 | 125 | # upon exit all the remote server variables are reset 126 | # so new remote server response will be taken by the usual way in connection.run() 127 | connection.logger.log('*** End of NTLM authorization process.\n') 128 | 129 | #----------------------------------------------------------------------- 130 | def www_ntlm_authentication(self, connection): 131 | "" 132 | connection.logger.log('*** Authorization in progress...\n') 133 | 134 | connection.close_rserver() 135 | 136 | # build an environment 137 | env = self.build_env_dict(connection) 138 | 139 | if env['NTLM_TO_BASIC']: 140 | got_credentials = self.translate_to_basic(env, connection, '401') 141 | 142 | if not got_credentials: 143 | connection.logger.log("*** Passing modified server's response to clent.\n") 144 | connection.logger.log('*** End of firts stage of NTLM translation.\n') 145 | return 146 | 147 | connection.connect_rserver() 148 | 149 | NTLM_msg1 = ntlm_messages.create_message1(env) 150 | connection.logger_auth.log(ntlm_messages.debug_message1(NTLM_msg1)) 151 | 152 | tmp_client_head_obj = connection.client_head_obj.copy() 153 | tmp_client_head_obj.replace_param_value('Connection', 'Keep-Alive') 154 | #tmp_client_head_obj.replace_param_value('Authorization', 'Negotiate ' + NTLM_msg1) 155 | tmp_client_head_obj.replace_param_value('Authorization', 'NTLM ' + NTLM_msg1) 156 | 157 | connection.reset_rserver() 158 | connection.rserver_buffer = '' 159 | connection.logger.log('*** Remote server buffer flushed.\n') 160 | 161 | # If we are POST/PUT-ing a large chunk of data we don't want 162 | # to do this at this time, so we change the data to 'abc' with 163 | # lenght = 3. 164 | if connection.client_head_obj.has_param('Content-Length'): 165 | tmp_client_head_obj.replace_param_value('Content-Length', '3') 166 | 167 | connection.logger.log('*** Fake NTLM header with Msg1:\n=====\n' + tmp_client_head_obj.__repr__()) 168 | connection.logger.log('*** Sending Fake NTLM header (and body) with Msg1...') 169 | tmp_client_head_obj.send(connection.rserver_socket) 170 | 171 | if connection.client_head_obj.has_param('Content-Length'): 172 | try: 173 | connection.rserver_socket.send('abc') 174 | except: 175 | # could not send data to remote server and have to end function 176 | connection.rserver_socket_closed = 1 177 | connection.logger.log('Failed.\n*** Could not send client data to remote server. Exception in send().\n') 178 | return 179 | 180 | connection.logger.log('Done.\n') 181 | 182 | connection.logger.log('*** Waiting for message 2 from the remote server...\n') 183 | while((not connection.rserver_all_got) and (not connection.rserver_socket_closed)): 184 | select.select([connection.rserver_socket.fileno()], [], [], 2.0) 185 | connection.run_rserver_loop() 186 | if connection.config['DEBUG']['SCR_DEBUG']: print '\b+', 187 | 188 | if connection.rserver_head_obj: 189 | connection.logger.log('*** Got NTLM message 2 from server.\n') 190 | # connection.logger.log('*** Remote server header with NTLM Msg2:\n=====\n' + connection.rserver_head_obj.__repr__()) 191 | else: 192 | # could not get response with msg2 from remote server and have to end function 193 | connection.logger.log('*** Could not get response with msg2 from server.\n') 194 | connection.logger.log('*** Stop Request = %d.\n' % connection.stop_request) 195 | return 196 | 197 | auth = connection.rserver_head_obj.get_param_values('Www-Authenticate') 198 | if auth: 199 | #connection.logger.log('### %s\n' % auth) 200 | msg2 = string.strip(string.split(auth[0])[1]) 201 | connection.logger_auth.log(ntlm_messages.debug_message2(msg2)) 202 | nonce = ntlm_messages.parse_message2(msg2) 203 | NTLM_msg3 = ntlm_messages.create_message3(nonce, env) 204 | connection.logger_auth.log(ntlm_messages.debug_message3(NTLM_msg3)) 205 | else: 206 | NTLM_msg3 = '' 207 | 208 | tmp_client_head_obj = connection.client_head_obj.copy() 209 | #tmp_client_head_obj.replace_param_value('Authorization', 'Negotiate ' + NTLM_msg3) 210 | tmp_client_head_obj.replace_param_value('Authorization', 'NTLM ' + NTLM_msg3) 211 | 212 | connection.reset_rserver() 213 | connection.rserver_buffer = '' 214 | connection.logger.log('*** Remote server buffer flushed.\n') 215 | connection.logger.log('*** Sending Fake NTLM header (not body) with Msg3...') 216 | tmp_client_head_obj.send(connection.rserver_socket) 217 | connection.logger.log('Done.\n') 218 | connection.logger.log('*** Fake NTLM header with Msg3:\n=====\n' + tmp_client_head_obj.__repr__()) 219 | 220 | # upon exit all the remote server variables are reset 221 | # so new remote server response will be taken by the usual way in connection.run() 222 | connection.logger.log('*** End of NTLM authorization process.\n') 223 | 224 | #----------------------------------------------------------------------- 225 | def build_env_dict(self, connection): 226 | "" 227 | connection.logger.log('*** Building environment for NTLM.\n') 228 | 229 | env = {} 230 | 231 | if connection.config['NTLM_AUTH']['NTLM_FLAGS']: 232 | env['FLAGS'] = connection.config['NTLM_AUTH']['NTLM_FLAGS'] 233 | 234 | connection.logger.log('*** Using custom NTLM flags: %s\n' % env['FLAGS']) 235 | 236 | else: 237 | # I have seen flag field '\005\202' as well (with NT response). 238 | #0x8206 or 0x8207 or 0x8205 239 | env['FLAGS'] = "06820000" 240 | #flags = utils.hex2str(ed['NTLM_FLAGS']) 241 | 242 | connection.logger.log('*** Using default NTLM flags: %s\n' % env['FLAGS']) 243 | 244 | 245 | env['LM'] = connection.config['NTLM_AUTH']['LM_PART'] 246 | env['NT'] = connection.config['NTLM_AUTH']['NT_PART'] 247 | 248 | # we must have at least LM part 249 | if not (env['LM'] or env['NT']): 250 | env['LM'] = 1 251 | 252 | if env['LM'] == 1 and env['NT'] == 0: 253 | connection.logger.log('*** NTLM version with LM response only.\n') 254 | 255 | elif env['LM'] == 1 and env['NT'] == 1: 256 | connection.logger.log('*** NTLM version with LM and NT responses.\n') 257 | 258 | elif env['LM'] == 0 and env['NT'] == 1: 259 | connection.logger.log('*** NTLM version with NT response only.\n') 260 | 261 | #env['UNICODE'] = connection.config['NTLM_AUTH']['UNICODE'] 262 | if env['NT']: 263 | env['UNICODE'] = 1 264 | else: 265 | env['UNICODE'] = 0 266 | 267 | # have to put these ones into [NTLM] section 268 | env['DOMAIN'] = string.upper(connection.config['NTLM_AUTH']['NT_DOMAIN']) 269 | 270 | # Check if there is explicit NT_Hostname in config, if there is one then take it, 271 | # if there is no one then take gethostname() result. 272 | if connection.config['NTLM_AUTH']['NT_HOSTNAME']: 273 | env['HOST'] = string.upper(connection.config['NTLM_AUTH']['NT_HOSTNAME']) 274 | else: 275 | env['HOST'] = string.upper(connection.config['GENERAL']['HOST']) 276 | 277 | env['USER'] = string.upper(connection.config['NTLM_AUTH']['USER']) 278 | 279 | connection.logger.log('*** NTLM Domain/Host/User: %s/%s/%s\n' % (env['DOMAIN'], env['HOST'], env['USER'])) 280 | 281 | # have to use UNICODE stings 282 | if env['UNICODE']: 283 | env['DOMAIN'] = utils.str2unicode(env['DOMAIN']) 284 | env['HOST'] = utils.str2unicode(env['HOST']) 285 | env['USER'] = utils.str2unicode(env['USER']) 286 | 287 | connection.logger.log('*** Using UNICODE stings.\n') 288 | 289 | 290 | if connection.config['NTLM_AUTH']['LM_HASHED_PW'] and connection.config['NTLM_AUTH']['NT_HASHED_PW']: 291 | env['LM_HASHED_PW'] = connection.config['NTLM_AUTH']['LM_HASHED_PW'] 292 | env['NT_HASHED_PW'] = connection.config['NTLM_AUTH']['NT_HASHED_PW'] 293 | 294 | connection.logger.log('*** NTLM hashed passwords found.\n') 295 | 296 | # Test params 297 | if connection.config['NTLM_AUTH'].has_key('NTLM_MODE'): 298 | env['NTLM_MODE'] = int(connection.config['NTLM_AUTH']['NTLM_MODE']) 299 | else: 300 | env['NTLM_MODE'] = 0 301 | 302 | # End of test params 303 | 304 | env['NTLM_TO_BASIC'] = connection.config['NTLM_AUTH']['NTLM_TO_BASIC'] 305 | 306 | connection.logger.log('*** Environment has been built successfully.\n') 307 | 308 | return env 309 | 310 | #----------------------------------------------------------------------- 311 | def translate_to_basic(self, environment, connection, error_code): 312 | "" 313 | connection.logger.log('*** Translating NTLM to Basic...\n') 314 | user, password = self.get_credentials_from_basic(connection, error_code) 315 | if user: 316 | connection.logger.log("*** Found Basic credentials in client's header.\n") 317 | 318 | if environment['UNICODE']: 319 | environment['USER'] = utils.str2unicode(string.upper(user)) 320 | else: 321 | environment['USER'] = string.upper(user) 322 | 323 | #environment['PASSWORD'] = password 324 | connection.logger.log("*** Basic User/Password: %s/%s.\n" % (user, password)) 325 | 326 | connection.logger.log("*** Calculating hashed passwords (LM and NT)...") 327 | environment['LM_HASHED_PW'] = ntlm_procs.create_LM_hashed_password(password) 328 | environment['NT_HASHED_PW'] = ntlm_procs.create_NT_hashed_password(password) 329 | connection.logger.log("Done.\n") 330 | 331 | return 1 332 | 333 | else: 334 | connection.logger.log("*** There are no basic credentials in client's header.\n") 335 | connection.logger.log("*** Replacing NTLM value with Basic in rserver's header...") 336 | self.replace_ntlm_with_basic(connection, error_code) 337 | connection.logger.log("Done.\n") 338 | 339 | connection.logger.log("*** New server's header:\n=====\n" + connection.rserver_head_obj.__repr__()) 340 | 341 | return 0 342 | 343 | #----------------------------------------------------------------------- 344 | def replace_ntlm_with_basic(self, connection, error_code): 345 | "" 346 | if error_code == '401': value_name = 'Www-Authenticate' 347 | else: value_name = 'Proxy-Authenticate' 348 | 349 | realm = connection.client_head_obj.get_http_server() 350 | basic_str = 'Basic realm="%s:%s"' % realm 351 | connection.rserver_head_obj.replace_param_value(value_name, basic_str) 352 | 353 | #----------------------------------------------------------------------- 354 | def get_credentials_from_basic(self, connection, error_code): 355 | "" 356 | if error_code == '401': value_name = 'Authorization' 357 | else: value_name = 'Proxy-Authorization' 358 | 359 | l = connection.client_head_obj.get_param_values(value_name) 360 | user, password = '', '' 361 | for i in l: 362 | t = string.split(i)[0] 363 | if string.lower(t) == 'basic': 364 | b64 = string.split(i)[1] 365 | cred = base64.decodestring(b64) 366 | user = string.split(cred, ':')[0] 367 | password = string.split(cred, ':')[1] 368 | 369 | return user, password 370 | 371 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 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) 19yy 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) 19yy 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 | -------------------------------------------------------------------------------- /ntlmaps/des_data.py: -------------------------------------------------------------------------------- 1 | # This file is part of 'NTLM Authorization Proxy Server' 2 | # Copyright 2001 Dmitry A. Rozmanov 3 | # 4 | # NTLM APS 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 | # NTLM APS 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 the sofware; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | 20 | from U32 import U32 21 | 22 | # static unsigned long des_SPtrans[8][64]={ 23 | 24 | des_SPtrans =\ 25 | [ 26 | #nibble 0 27 | [ 28 | U32(0x00820200L), U32(0x00020000L), U32(0x80800000L), U32(0x80820200L), 29 | U32(0x00800000L), U32(0x80020200L), U32(0x80020000L), U32(0x80800000L), 30 | U32(0x80020200L), U32(0x00820200L), U32(0x00820000L), U32(0x80000200L), 31 | U32(0x80800200L), U32(0x00800000L), U32(0x00000000L), U32(0x80020000L), 32 | U32(0x00020000L), U32(0x80000000L), U32(0x00800200L), U32(0x00020200L), 33 | U32(0x80820200L), U32(0x00820000L), U32(0x80000200L), U32(0x00800200L), 34 | U32(0x80000000L), U32(0x00000200L), U32(0x00020200L), U32(0x80820000L), 35 | U32(0x00000200L), U32(0x80800200L), U32(0x80820000L), U32(0x00000000L), 36 | U32(0x00000000L), U32(0x80820200L), U32(0x00800200L), U32(0x80020000L), 37 | U32(0x00820200L), U32(0x00020000L), U32(0x80000200L), U32(0x00800200L), 38 | U32(0x80820000L), U32(0x00000200L), U32(0x00020200L), U32(0x80800000L), 39 | U32(0x80020200L), U32(0x80000000L), U32(0x80800000L), U32(0x00820000L), 40 | U32(0x80820200L), U32(0x00020200L), U32(0x00820000L), U32(0x80800200L), 41 | U32(0x00800000L), U32(0x80000200L), U32(0x80020000L), U32(0x00000000L), 42 | U32(0x00020000L), U32(0x00800000L), U32(0x80800200L), U32(0x00820200L), 43 | U32(0x80000000L), U32(0x80820000L), U32(0x00000200L), U32(0x80020200L), 44 | ], 45 | 46 | #nibble 1 47 | [ 48 | U32(0x10042004L), U32(0x00000000L), U32(0x00042000L), U32(0x10040000L), 49 | U32(0x10000004L), U32(0x00002004L), U32(0x10002000L), U32(0x00042000L), 50 | U32(0x00002000L), U32(0x10040004L), U32(0x00000004L), U32(0x10002000L), 51 | U32(0x00040004L), U32(0x10042000L), U32(0x10040000L), U32(0x00000004L), 52 | U32(0x00040000L), U32(0x10002004L), U32(0x10040004L), U32(0x00002000L), 53 | U32(0x00042004L), U32(0x10000000L), U32(0x00000000L), U32(0x00040004L), 54 | U32(0x10002004L), U32(0x00042004L), U32(0x10042000L), U32(0x10000004L), 55 | U32(0x10000000L), U32(0x00040000L), U32(0x00002004L), U32(0x10042004L), 56 | U32(0x00040004L), U32(0x10042000L), U32(0x10002000L), U32(0x00042004L), 57 | U32(0x10042004L), U32(0x00040004L), U32(0x10000004L), U32(0x00000000L), 58 | U32(0x10000000L), U32(0x00002004L), U32(0x00040000L), U32(0x10040004L), 59 | U32(0x00002000L), U32(0x10000000L), U32(0x00042004L), U32(0x10002004L), 60 | U32(0x10042000L), U32(0x00002000L), U32(0x00000000L), U32(0x10000004L), 61 | U32(0x00000004L), U32(0x10042004L), U32(0x00042000L), U32(0x10040000L), 62 | U32(0x10040004L), U32(0x00040000L), U32(0x00002004L), U32(0x10002000L), 63 | U32(0x10002004L), U32(0x00000004L), U32(0x10040000L), U32(0x00042000L), 64 | ], 65 | 66 | #nibble 2 67 | [ 68 | U32(0x41000000L), U32(0x01010040L), U32(0x00000040L), U32(0x41000040L), 69 | U32(0x40010000L), U32(0x01000000L), U32(0x41000040L), U32(0x00010040L), 70 | U32(0x01000040L), U32(0x00010000L), U32(0x01010000L), U32(0x40000000L), 71 | U32(0x41010040L), U32(0x40000040L), U32(0x40000000L), U32(0x41010000L), 72 | U32(0x00000000L), U32(0x40010000L), U32(0x01010040L), U32(0x00000040L), 73 | U32(0x40000040L), U32(0x41010040L), U32(0x00010000L), U32(0x41000000L), 74 | U32(0x41010000L), U32(0x01000040L), U32(0x40010040L), U32(0x01010000L), 75 | U32(0x00010040L), U32(0x00000000L), U32(0x01000000L), U32(0x40010040L), 76 | U32(0x01010040L), U32(0x00000040L), U32(0x40000000L), U32(0x00010000L), 77 | U32(0x40000040L), U32(0x40010000L), U32(0x01010000L), U32(0x41000040L), 78 | U32(0x00000000L), U32(0x01010040L), U32(0x00010040L), U32(0x41010000L), 79 | U32(0x40010000L), U32(0x01000000L), U32(0x41010040L), U32(0x40000000L), 80 | U32(0x40010040L), U32(0x41000000L), U32(0x01000000L), U32(0x41010040L), 81 | U32(0x00010000L), U32(0x01000040L), U32(0x41000040L), U32(0x00010040L), 82 | U32(0x01000040L), U32(0x00000000L), U32(0x41010000L), U32(0x40000040L), 83 | U32(0x41000000L), U32(0x40010040L), U32(0x00000040L), U32(0x01010000L), 84 | ], 85 | 86 | #nibble 3 87 | [ 88 | U32(0x00100402L), U32(0x04000400L), U32(0x00000002L), U32(0x04100402L), 89 | U32(0x00000000L), U32(0x04100000L), U32(0x04000402L), U32(0x00100002L), 90 | U32(0x04100400L), U32(0x04000002L), U32(0x04000000L), U32(0x00000402L), 91 | U32(0x04000002L), U32(0x00100402L), U32(0x00100000L), U32(0x04000000L), 92 | U32(0x04100002L), U32(0x00100400L), U32(0x00000400L), U32(0x00000002L), 93 | U32(0x00100400L), U32(0x04000402L), U32(0x04100000L), U32(0x00000400L), 94 | U32(0x00000402L), U32(0x00000000L), U32(0x00100002L), U32(0x04100400L), 95 | U32(0x04000400L), U32(0x04100002L), U32(0x04100402L), U32(0x00100000L), 96 | U32(0x04100002L), U32(0x00000402L), U32(0x00100000L), U32(0x04000002L), 97 | U32(0x00100400L), U32(0x04000400L), U32(0x00000002L), U32(0x04100000L), 98 | U32(0x04000402L), U32(0x00000000L), U32(0x00000400L), U32(0x00100002L), 99 | U32(0x00000000L), U32(0x04100002L), U32(0x04100400L), U32(0x00000400L), 100 | U32(0x04000000L), U32(0x04100402L), U32(0x00100402L), U32(0x00100000L), 101 | U32(0x04100402L), U32(0x00000002L), U32(0x04000400L), U32(0x00100402L), 102 | U32(0x00100002L), U32(0x00100400L), U32(0x04100000L), U32(0x04000402L), 103 | U32(0x00000402L), U32(0x04000000L), U32(0x04000002L), U32(0x04100400L), 104 | ], 105 | 106 | #nibble 4 107 | [ 108 | U32(0x02000000L), U32(0x00004000L), U32(0x00000100L), U32(0x02004108L), 109 | U32(0x02004008L), U32(0x02000100L), U32(0x00004108L), U32(0x02004000L), 110 | U32(0x00004000L), U32(0x00000008L), U32(0x02000008L), U32(0x00004100L), 111 | U32(0x02000108L), U32(0x02004008L), U32(0x02004100L), U32(0x00000000L), 112 | U32(0x00004100L), U32(0x02000000L), U32(0x00004008L), U32(0x00000108L), 113 | U32(0x02000100L), U32(0x00004108L), U32(0x00000000L), U32(0x02000008L), 114 | U32(0x00000008L), U32(0x02000108L), U32(0x02004108L), U32(0x00004008L), 115 | U32(0x02004000L), U32(0x00000100L), U32(0x00000108L), U32(0x02004100L), 116 | U32(0x02004100L), U32(0x02000108L), U32(0x00004008L), U32(0x02004000L), 117 | U32(0x00004000L), U32(0x00000008L), U32(0x02000008L), U32(0x02000100L), 118 | U32(0x02000000L), U32(0x00004100L), U32(0x02004108L), U32(0x00000000L), 119 | U32(0x00004108L), U32(0x02000000L), U32(0x00000100L), U32(0x00004008L), 120 | U32(0x02000108L), U32(0x00000100L), U32(0x00000000L), U32(0x02004108L), 121 | U32(0x02004008L), U32(0x02004100L), U32(0x00000108L), U32(0x00004000L), 122 | U32(0x00004100L), U32(0x02004008L), U32(0x02000100L), U32(0x00000108L), 123 | U32(0x00000008L), U32(0x00004108L), U32(0x02004000L), U32(0x02000008L), 124 | ], 125 | 126 | #nibble 5 127 | [ 128 | U32(0x20000010L), U32(0x00080010L), U32(0x00000000L), U32(0x20080800L), 129 | U32(0x00080010L), U32(0x00000800L), U32(0x20000810L), U32(0x00080000L), 130 | U32(0x00000810L), U32(0x20080810L), U32(0x00080800L), U32(0x20000000L), 131 | U32(0x20000800L), U32(0x20000010L), U32(0x20080000L), U32(0x00080810L), 132 | U32(0x00080000L), U32(0x20000810L), U32(0x20080010L), U32(0x00000000L), 133 | U32(0x00000800L), U32(0x00000010L), U32(0x20080800L), U32(0x20080010L), 134 | U32(0x20080810L), U32(0x20080000L), U32(0x20000000L), U32(0x00000810L), 135 | U32(0x00000010L), U32(0x00080800L), U32(0x00080810L), U32(0x20000800L), 136 | U32(0x00000810L), U32(0x20000000L), U32(0x20000800L), U32(0x00080810L), 137 | U32(0x20080800L), U32(0x00080010L), U32(0x00000000L), U32(0x20000800L), 138 | U32(0x20000000L), U32(0x00000800L), U32(0x20080010L), U32(0x00080000L), 139 | U32(0x00080010L), U32(0x20080810L), U32(0x00080800L), U32(0x00000010L), 140 | U32(0x20080810L), U32(0x00080800L), U32(0x00080000L), U32(0x20000810L), 141 | U32(0x20000010L), U32(0x20080000L), U32(0x00080810L), U32(0x00000000L), 142 | U32(0x00000800L), U32(0x20000010L), U32(0x20000810L), U32(0x20080800L), 143 | U32(0x20080000L), U32(0x00000810L), U32(0x00000010L), U32(0x20080010L), 144 | ], 145 | 146 | #nibble 6 147 | [ 148 | U32(0x00001000L), U32(0x00000080L), U32(0x00400080L), U32(0x00400001L), 149 | U32(0x00401081L), U32(0x00001001L), U32(0x00001080L), U32(0x00000000L), 150 | U32(0x00400000L), U32(0x00400081L), U32(0x00000081L), U32(0x00401000L), 151 | U32(0x00000001L), U32(0x00401080L), U32(0x00401000L), U32(0x00000081L), 152 | U32(0x00400081L), U32(0x00001000L), U32(0x00001001L), U32(0x00401081L), 153 | U32(0x00000000L), U32(0x00400080L), U32(0x00400001L), U32(0x00001080L), 154 | U32(0x00401001L), U32(0x00001081L), U32(0x00401080L), U32(0x00000001L), 155 | U32(0x00001081L), U32(0x00401001L), U32(0x00000080L), U32(0x00400000L), 156 | U32(0x00001081L), U32(0x00401000L), U32(0x00401001L), U32(0x00000081L), 157 | U32(0x00001000L), U32(0x00000080L), U32(0x00400000L), U32(0x00401001L), 158 | U32(0x00400081L), U32(0x00001081L), U32(0x00001080L), U32(0x00000000L), 159 | U32(0x00000080L), U32(0x00400001L), U32(0x00000001L), U32(0x00400080L), 160 | U32(0x00000000L), U32(0x00400081L), U32(0x00400080L), U32(0x00001080L), 161 | U32(0x00000081L), U32(0x00001000L), U32(0x00401081L), U32(0x00400000L), 162 | U32(0x00401080L), U32(0x00000001L), U32(0x00001001L), U32(0x00401081L), 163 | U32(0x00400001L), U32(0x00401080L), U32(0x00401000L), U32(0x00001001L), 164 | ], 165 | 166 | #nibble 7 167 | [ 168 | U32(0x08200020L), U32(0x08208000L), U32(0x00008020L), U32(0x00000000L), 169 | U32(0x08008000L), U32(0x00200020L), U32(0x08200000L), U32(0x08208020L), 170 | U32(0x00000020L), U32(0x08000000L), U32(0x00208000L), U32(0x00008020L), 171 | U32(0x00208020L), U32(0x08008020L), U32(0x08000020L), U32(0x08200000L), 172 | U32(0x00008000L), U32(0x00208020L), U32(0x00200020L), U32(0x08008000L), 173 | U32(0x08208020L), U32(0x08000020L), U32(0x00000000L), U32(0x00208000L), 174 | U32(0x08000000L), U32(0x00200000L), U32(0x08008020L), U32(0x08200020L), 175 | U32(0x00200000L), U32(0x00008000L), U32(0x08208000L), U32(0x00000020L), 176 | U32(0x00200000L), U32(0x00008000L), U32(0x08000020L), U32(0x08208020L), 177 | U32(0x00008020L), U32(0x08000000L), U32(0x00000000L), U32(0x00208000L), 178 | U32(0x08200020L), U32(0x08008020L), U32(0x08008000L), U32(0x00200020L), 179 | U32(0x08208000L), U32(0x00000020L), U32(0x00200020L), U32(0x08008000L), 180 | U32(0x08208020L), U32(0x00200000L), U32(0x08200000L), U32(0x08000020L), 181 | U32(0x00208000L), U32(0x00008020L), U32(0x08008020L), U32(0x08200000L), 182 | U32(0x00000020L), U32(0x08208000L), U32(0x00208020L), U32(0x00000000L), 183 | U32(0x08000000L), U32(0x08200020L), U32(0x00008000L), U32(0x00208020L), 184 | ], 185 | ] 186 | 187 | #static unsigned long des_skb[8][64]={ 188 | 189 | des_skb = \ 190 | [ 191 | #for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 192 | [ 193 | U32(0x00000000L),U32(0x00000010L),U32(0x20000000L),U32(0x20000010L), 194 | U32(0x00010000L),U32(0x00010010L),U32(0x20010000L),U32(0x20010010L), 195 | U32(0x00000800L),U32(0x00000810L),U32(0x20000800L),U32(0x20000810L), 196 | U32(0x00010800L),U32(0x00010810L),U32(0x20010800L),U32(0x20010810L), 197 | U32(0x00000020L),U32(0x00000030L),U32(0x20000020L),U32(0x20000030L), 198 | U32(0x00010020L),U32(0x00010030L),U32(0x20010020L),U32(0x20010030L), 199 | U32(0x00000820L),U32(0x00000830L),U32(0x20000820L),U32(0x20000830L), 200 | U32(0x00010820L),U32(0x00010830L),U32(0x20010820L),U32(0x20010830L), 201 | U32(0x00080000L),U32(0x00080010L),U32(0x20080000L),U32(0x20080010L), 202 | U32(0x00090000L),U32(0x00090010L),U32(0x20090000L),U32(0x20090010L), 203 | U32(0x00080800L),U32(0x00080810L),U32(0x20080800L),U32(0x20080810L), 204 | U32(0x00090800L),U32(0x00090810L),U32(0x20090800L),U32(0x20090810L), 205 | U32(0x00080020L),U32(0x00080030L),U32(0x20080020L),U32(0x20080030L), 206 | U32(0x00090020L),U32(0x00090030L),U32(0x20090020L),U32(0x20090030L), 207 | U32(0x00080820L),U32(0x00080830L),U32(0x20080820L),U32(0x20080830L), 208 | U32(0x00090820L),U32(0x00090830L),U32(0x20090820L),U32(0x20090830L), 209 | ], 210 | 211 | #for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 212 | [ 213 | U32(0x00000000L),U32(0x02000000L),U32(0x00002000L),U32(0x02002000L), 214 | U32(0x00200000L),U32(0x02200000L),U32(0x00202000L),U32(0x02202000L), 215 | U32(0x00000004L),U32(0x02000004L),U32(0x00002004L),U32(0x02002004L), 216 | U32(0x00200004L),U32(0x02200004L),U32(0x00202004L),U32(0x02202004L), 217 | U32(0x00000400L),U32(0x02000400L),U32(0x00002400L),U32(0x02002400L), 218 | U32(0x00200400L),U32(0x02200400L),U32(0x00202400L),U32(0x02202400L), 219 | U32(0x00000404L),U32(0x02000404L),U32(0x00002404L),U32(0x02002404L), 220 | U32(0x00200404L),U32(0x02200404L),U32(0x00202404L),U32(0x02202404L), 221 | U32(0x10000000L),U32(0x12000000L),U32(0x10002000L),U32(0x12002000L), 222 | U32(0x10200000L),U32(0x12200000L),U32(0x10202000L),U32(0x12202000L), 223 | U32(0x10000004L),U32(0x12000004L),U32(0x10002004L),U32(0x12002004L), 224 | U32(0x10200004L),U32(0x12200004L),U32(0x10202004L),U32(0x12202004L), 225 | U32(0x10000400L),U32(0x12000400L),U32(0x10002400L),U32(0x12002400L), 226 | U32(0x10200400L),U32(0x12200400L),U32(0x10202400L),U32(0x12202400L), 227 | U32(0x10000404L),U32(0x12000404L),U32(0x10002404L),U32(0x12002404L), 228 | U32(0x10200404L),U32(0x12200404L),U32(0x10202404L),U32(0x12202404L), 229 | ], 230 | 231 | #for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 232 | [ 233 | U32(0x00000000L),U32(0x00000001L),U32(0x00040000L),U32(0x00040001L), 234 | U32(0x01000000L),U32(0x01000001L),U32(0x01040000L),U32(0x01040001L), 235 | U32(0x00000002L),U32(0x00000003L),U32(0x00040002L),U32(0x00040003L), 236 | U32(0x01000002L),U32(0x01000003L),U32(0x01040002L),U32(0x01040003L), 237 | U32(0x00000200L),U32(0x00000201L),U32(0x00040200L),U32(0x00040201L), 238 | U32(0x01000200L),U32(0x01000201L),U32(0x01040200L),U32(0x01040201L), 239 | U32(0x00000202L),U32(0x00000203L),U32(0x00040202L),U32(0x00040203L), 240 | U32(0x01000202L),U32(0x01000203L),U32(0x01040202L),U32(0x01040203L), 241 | U32(0x08000000L),U32(0x08000001L),U32(0x08040000L),U32(0x08040001L), 242 | U32(0x09000000L),U32(0x09000001L),U32(0x09040000L),U32(0x09040001L), 243 | U32(0x08000002L),U32(0x08000003L),U32(0x08040002L),U32(0x08040003L), 244 | U32(0x09000002L),U32(0x09000003L),U32(0x09040002L),U32(0x09040003L), 245 | U32(0x08000200L),U32(0x08000201L),U32(0x08040200L),U32(0x08040201L), 246 | U32(0x09000200L),U32(0x09000201L),U32(0x09040200L),U32(0x09040201L), 247 | U32(0x08000202L),U32(0x08000203L),U32(0x08040202L),U32(0x08040203L), 248 | U32(0x09000202L),U32(0x09000203L),U32(0x09040202L),U32(0x09040203L), 249 | ], 250 | 251 | #for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 252 | [ 253 | U32(0x00000000L),U32(0x00100000L),U32(0x00000100L),U32(0x00100100L), 254 | U32(0x00000008L),U32(0x00100008L),U32(0x00000108L),U32(0x00100108L), 255 | U32(0x00001000L),U32(0x00101000L),U32(0x00001100L),U32(0x00101100L), 256 | U32(0x00001008L),U32(0x00101008L),U32(0x00001108L),U32(0x00101108L), 257 | U32(0x04000000L),U32(0x04100000L),U32(0x04000100L),U32(0x04100100L), 258 | U32(0x04000008L),U32(0x04100008L),U32(0x04000108L),U32(0x04100108L), 259 | U32(0x04001000L),U32(0x04101000L),U32(0x04001100L),U32(0x04101100L), 260 | U32(0x04001008L),U32(0x04101008L),U32(0x04001108L),U32(0x04101108L), 261 | U32(0x00020000L),U32(0x00120000L),U32(0x00020100L),U32(0x00120100L), 262 | U32(0x00020008L),U32(0x00120008L),U32(0x00020108L),U32(0x00120108L), 263 | U32(0x00021000L),U32(0x00121000L),U32(0x00021100L),U32(0x00121100L), 264 | U32(0x00021008L),U32(0x00121008L),U32(0x00021108L),U32(0x00121108L), 265 | U32(0x04020000L),U32(0x04120000L),U32(0x04020100L),U32(0x04120100L), 266 | U32(0x04020008L),U32(0x04120008L),U32(0x04020108L),U32(0x04120108L), 267 | U32(0x04021000L),U32(0x04121000L),U32(0x04021100L),U32(0x04121100L), 268 | U32(0x04021008L),U32(0x04121008L),U32(0x04021108L),U32(0x04121108L), 269 | ], 270 | 271 | #for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 272 | [ 273 | U32(0x00000000L),U32(0x10000000L),U32(0x00010000L),U32(0x10010000L), 274 | U32(0x00000004L),U32(0x10000004L),U32(0x00010004L),U32(0x10010004L), 275 | U32(0x20000000L),U32(0x30000000L),U32(0x20010000L),U32(0x30010000L), 276 | U32(0x20000004L),U32(0x30000004L),U32(0x20010004L),U32(0x30010004L), 277 | U32(0x00100000L),U32(0x10100000L),U32(0x00110000L),U32(0x10110000L), 278 | U32(0x00100004L),U32(0x10100004L),U32(0x00110004L),U32(0x10110004L), 279 | U32(0x20100000L),U32(0x30100000L),U32(0x20110000L),U32(0x30110000L), 280 | U32(0x20100004L),U32(0x30100004L),U32(0x20110004L),U32(0x30110004L), 281 | U32(0x00001000L),U32(0x10001000L),U32(0x00011000L),U32(0x10011000L), 282 | U32(0x00001004L),U32(0x10001004L),U32(0x00011004L),U32(0x10011004L), 283 | U32(0x20001000L),U32(0x30001000L),U32(0x20011000L),U32(0x30011000L), 284 | U32(0x20001004L),U32(0x30001004L),U32(0x20011004L),U32(0x30011004L), 285 | U32(0x00101000L),U32(0x10101000L),U32(0x00111000L),U32(0x10111000L), 286 | U32(0x00101004L),U32(0x10101004L),U32(0x00111004L),U32(0x10111004L), 287 | U32(0x20101000L),U32(0x30101000L),U32(0x20111000L),U32(0x30111000L), 288 | U32(0x20101004L),U32(0x30101004L),U32(0x20111004L),U32(0x30111004L), 289 | ], 290 | 291 | #for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 292 | [ 293 | U32(0x00000000L),U32(0x08000000L),U32(0x00000008L),U32(0x08000008L), 294 | U32(0x00000400L),U32(0x08000400L),U32(0x00000408L),U32(0x08000408L), 295 | U32(0x00020000L),U32(0x08020000L),U32(0x00020008L),U32(0x08020008L), 296 | U32(0x00020400L),U32(0x08020400L),U32(0x00020408L),U32(0x08020408L), 297 | U32(0x00000001L),U32(0x08000001L),U32(0x00000009L),U32(0x08000009L), 298 | U32(0x00000401L),U32(0x08000401L),U32(0x00000409L),U32(0x08000409L), 299 | U32(0x00020001L),U32(0x08020001L),U32(0x00020009L),U32(0x08020009L), 300 | U32(0x00020401L),U32(0x08020401L),U32(0x00020409L),U32(0x08020409L), 301 | U32(0x02000000L),U32(0x0A000000L),U32(0x02000008L),U32(0x0A000008L), 302 | U32(0x02000400L),U32(0x0A000400L),U32(0x02000408L),U32(0x0A000408L), 303 | U32(0x02020000L),U32(0x0A020000L),U32(0x02020008L),U32(0x0A020008L), 304 | U32(0x02020400L),U32(0x0A020400L),U32(0x02020408L),U32(0x0A020408L), 305 | U32(0x02000001L),U32(0x0A000001L),U32(0x02000009L),U32(0x0A000009L), 306 | U32(0x02000401L),U32(0x0A000401L),U32(0x02000409L),U32(0x0A000409L), 307 | U32(0x02020001L),U32(0x0A020001L),U32(0x02020009L),U32(0x0A020009L), 308 | U32(0x02020401L),U32(0x0A020401L),U32(0x02020409L),U32(0x0A020409L), 309 | ], 310 | 311 | #for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 312 | [ 313 | U32(0x00000000L),U32(0x00000100L),U32(0x00080000L),U32(0x00080100L), 314 | U32(0x01000000L),U32(0x01000100L),U32(0x01080000L),U32(0x01080100L), 315 | U32(0x00000010L),U32(0x00000110L),U32(0x00080010L),U32(0x00080110L), 316 | U32(0x01000010L),U32(0x01000110L),U32(0x01080010L),U32(0x01080110L), 317 | U32(0x00200000L),U32(0x00200100L),U32(0x00280000L),U32(0x00280100L), 318 | U32(0x01200000L),U32(0x01200100L),U32(0x01280000L),U32(0x01280100L), 319 | U32(0x00200010L),U32(0x00200110L),U32(0x00280010L),U32(0x00280110L), 320 | U32(0x01200010L),U32(0x01200110L),U32(0x01280010L),U32(0x01280110L), 321 | U32(0x00000200L),U32(0x00000300L),U32(0x00080200L),U32(0x00080300L), 322 | U32(0x01000200L),U32(0x01000300L),U32(0x01080200L),U32(0x01080300L), 323 | U32(0x00000210L),U32(0x00000310L),U32(0x00080210L),U32(0x00080310L), 324 | U32(0x01000210L),U32(0x01000310L),U32(0x01080210L),U32(0x01080310L), 325 | U32(0x00200200L),U32(0x00200300L),U32(0x00280200L),U32(0x00280300L), 326 | U32(0x01200200L),U32(0x01200300L),U32(0x01280200L),U32(0x01280300L), 327 | U32(0x00200210L),U32(0x00200310L),U32(0x00280210L),U32(0x00280310L), 328 | U32(0x01200210L),U32(0x01200310L),U32(0x01280210L),U32(0x01280310L), 329 | ], 330 | 331 | #for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 332 | [ 333 | U32(0x00000000L),U32(0x04000000L),U32(0x00040000L),U32(0x04040000L), 334 | U32(0x00000002L),U32(0x04000002L),U32(0x00040002L),U32(0x04040002L), 335 | U32(0x00002000L),U32(0x04002000L),U32(0x00042000L),U32(0x04042000L), 336 | U32(0x00002002L),U32(0x04002002L),U32(0x00042002L),U32(0x04042002L), 337 | U32(0x00000020L),U32(0x04000020L),U32(0x00040020L),U32(0x04040020L), 338 | U32(0x00000022L),U32(0x04000022L),U32(0x00040022L),U32(0x04040022L), 339 | U32(0x00002020L),U32(0x04002020L),U32(0x00042020L),U32(0x04042020L), 340 | U32(0x00002022L),U32(0x04002022L),U32(0x00042022L),U32(0x04042022L), 341 | U32(0x00000800L),U32(0x04000800L),U32(0x00040800L),U32(0x04040800L), 342 | U32(0x00000802L),U32(0x04000802L),U32(0x00040802L),U32(0x04040802L), 343 | U32(0x00002800L),U32(0x04002800L),U32(0x00042800L),U32(0x04042800L), 344 | U32(0x00002802L),U32(0x04002802L),U32(0x00042802L),U32(0x04042802L), 345 | U32(0x00000820L),U32(0x04000820L),U32(0x00040820L),U32(0x04040820L), 346 | U32(0x00000822L),U32(0x04000822L),U32(0x00040822L),U32(0x04040822L), 347 | U32(0x00002820L),U32(0x04002820L),U32(0x00042820L),U32(0x04042820L), 348 | U32(0x00002822L),U32(0x04002822L),U32(0x00042822L),U32(0x04042822L), 349 | ] 350 | 351 | ] --------------------------------------------------------------------------------