ntlmaps-0.9.9.0.1/0000775000076400007640000000000010366274735013067 5ustar dixonddixondntlmaps-0.9.9.0.1/doc/0000775000076400007640000000000010366274735013634 5ustar dixonddixondntlmaps-0.9.9.0.1/doc/greenball.gif0000664000076400007640000000050610252205210016227 0ustar dixonddixondGIF89aZ J "kBU0Z 1F)cqsJU's!?1?{Ƅ){Ƈ4J$cuc!!,cpH,ďP|FcLōEpx %҆x* C7p(yC  wD$("E G HDA;ntlmaps-0.9.9.0.1/doc/ENCRYPTION.txt0000664000076400007640000003267710252205210016056 0ustar dixonddixond!== !== ENCRYPTION.txt for Samba release 2.2.0-alpha1 23 Nov 2000 !== Contributor: Jeremy Allison Updated: April 19, 1999 Note: Please refer to WinNT.txt also Subject: LanManager / Samba Password Encryption. ============================================================================ With the development of LanManager and Windows NT compatible password encryption for Samba, it is now able to validate user connections in exactly the same way as a LanManager or Windows NT server. This document describes how the SMB password encryption algorithm works and what issues there are in choosing whether you want to use it. You should read it carefully, especially the part about security and the "PROS and CONS" section. How does it work ? ------------------ LanManager encryption is somewhat similar to UNIX password encryption. The server uses a file containing a hashed value of a user's password. This is created by taking the user's plaintext password, capitalising it, and either truncating to 14 bytes (or padding to 14 bytes with null bytes). This 14 byte value is used as two 56 bit DES keys to encrypt a 'magic' eight byte value, forming a 16 byte value which is stored by the server and client. Let this value be known as the *hashed password*. Windows NT encryption is a higher quality mechanism, consisting of doing an MD4 hash on a Unicode version of the user's password. This also produces a 16 byte hash value that is non-reversible. When a client (LanManager, Windows for WorkGroups, Windows 95 or Windows NT) wishes to mount a Samba drive (or use a Samba resource) it first requests a connection and negotiates the protocol that the client and server will use. In the reply to this request the Samba server generates and appends an 8 byte, random value - this is stored in the Samba server after the reply is sent and is known as the *challenge*. The challenge is different for every client connection. The client then uses the hashed password (16 byte values described above), appended with 5 null bytes, as three 56 bit DES keys, each of which is used to encrypt the challenge 8 byte value, forming a 24 byte value known as the *response*. In the SMB call SMBsessionsetupX (when user level security is selected) or the call SMBtconX (when share level security is selected) the 24 byte response is returned by the client to the Samba server. For Windows NT protocol levels the above calculation is done on both hashes of the user's password and both responses are returned in the SMB call, giving two 24 byte values. The Samba server then reproduces the above calculation, using its own stored value of the 16 byte hashed password (read from the smbpasswd file - described later) and the challenge value that it kept from the negotiate protocol reply. It then checks to see if the 24 byte value it calculates matches the 24 byte value returned to it from the client. If these values match exactly, then the client knew the correct password (or the 16 byte hashed value - see security note below) and is thus allowed access. If not, then the client did not know the correct password and is denied access. Note that the Samba server never knows or stores the cleartext of the user's password - just the 16 byte hashed values derived from it. Also note that the cleartext password or 16 byte hashed values are never transmitted over the network - thus increasing security. IMPORTANT NOTE ABOUT SECURITY ----------------------------- The unix and SMB password encryption techniques seem similar on the surface. This similarity is, however, only skin deep. The unix scheme typically sends clear text passwords over the nextwork when logging in. This is bad. The SMB encryption scheme never sends the cleartext password over the network but it does store the 16 byte hashed values on disk. This is also bad. Why? Because the 16 byte hashed values are a "password equivalent". You cannot derive the user's password from them, but they could potentially be used in a modified client to gain access to a server. This would require considerable technical knowledge on behalf of the attacker but is perfectly possible. You should thus treat the smbpasswd file as though it contained the cleartext passwords of all your users. Its contents must be kept secret, and the file should be protected accordingly. Ideally we would like a password scheme which neither requires plain text passwords on the net or on disk. Unfortunately this is not available as Samba is stuck with being compatible with other SMB systems (WinNT, WfWg, Win95 etc). PROS AND CONS ------------- There are advantages and disadvantages to both schemes. Advantages of SMB Encryption: ----------------------------- - plain text passwords are not passed across the network. Someone using a network sniffer cannot just record passwords going to the SMB server. - WinNT doesn't like talking to a server that isn't using SMB encrypted passwords. It will refuse to browse the server if the server is also in user level security mode. It will insist on prompting the user for the password on each connection, which is very annoying. The only things you can do to stop this is to use SMB encryption. Advantages of non-encrypted passwords: -------------------------------------- - plain text passwords are not kept on disk. - uses same password file as other unix services such as login and ftp - you are probably already using other services (such as telnet and ftp) which send plain text passwords over the net, so not sending them for SMB isn't such a big deal. Note that Windows NT 4.0 Service pack 3 changed the default for permissible authentication so that plaintext passwords are *never* sent over the wire. The solution to this is either to switch to encrypted passwords with Samba or edit the Windows NT registry to re-enable plaintext passwords. See the document WinNT.txt for details on how to do this. The smbpasswd file. ------------------- In order for Samba to participate in the above protocol it must be able to look up the 16 byte hashed values given a user name. Unfortunately, as the UNIX password value is also a one way hash function (ie. it is impossible to retrieve the cleartext of the user's password given the UNIX hash of it) then a separate password file containing this 16 byte value must be kept. To minimise problems with these two password files, getting out of sync, the UNIX /etc/passwd and the smbpasswd file, a utility, mksmbpasswd.sh, is provided to generate a smbpasswd file from a UNIX /etc/passwd file. To generate the smbpasswd file from your /etc/passwd file use the following command :- cat /etc/passwd | mksmbpasswd.sh >/usr/local/samba/private/smbpasswd If you are running on a system that uses NIS, use ypcat passwd | mksmbpasswd.sh >/usr/local/samba/private/smbpasswd The mksmbpasswd.sh program is found in the Samba source directory. By default, the smbpasswd file is stored in :- /usr/local/samba/private/smbpasswd The owner of the /usr/local/samba/private directory should be set to root, and the permissions on it should be set to :- r-x------ The command chmod 500 /usr/local/samba/private will do the trick. Likewise, the smbpasswd file inside the private directory should be owned by root and the permissions on is should be set to rw------- by the command :- chmod 600 smbpasswd. The format of the smbpasswd file is username:uid:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[Account type]:LCT-:Long name Although only the username, uid, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX, [Account type] and last-change-time sections are significant and are looked at in the Samba code. It is *VITALLY* important that there by 32 'X' characters between the two ':' characters in the XXX sections - the smbpasswd and Samba code will fail to validate any entries that do not have 32 characters between ':' characters. The first XXX section is for the Lanman password hash, the second is for the Windows NT version. When the password file is created all users have password entries consisting of 32 'X' characters. By default this disallows any access as this user. When a user has a password set, the 'X' characters change to 32 ascii hexadecimal digits (0-9, A-F). These are an ascii representation of the 16 byte hashed value of a user's password. To set a user to have no password (not recommended), edit the file using vi, and replace the first 11 characters with the asci text NO PASSWORD Eg. To clear the password for user bob, his smbpasswd file entry would look like : bob:100:NO PASSWORDXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U ]:LCT-00000000:Bob's full name:/bobhome:/bobshell If you are allowing users to use the smbpasswd command to set their own passwords, you may want to give users NO PASSWORD initially so they do not have to enter a previous password when changing to their new password (not recommended). In order for you to allow this the smbpasswd program must be able to connect to the smbd daemon as that user with no password. Enable this by adding the line : null passwords = true to the [global] section of the smb.conf file (this is why the above scenario is not recommended). Preferably, allocate your users a default password to begin with, so you do not have to enable this on your server. Note : This file should be protected very carefully. Anyone with access to this file can (with enough knowledge of the protocols) gain access to your SMB server. The file is thus more sensitive than a normal unix /etc/passwd file. The smbpasswd Command. ---------------------- The smbpasswd command maintains the two 32 byte password fields in the smbpasswd file. If you wish to make it similar to the unix passwd or yppasswd programs, install it in /usr/local/samba/bin (or your main Samba binary directory). Note that as of Samba 1.9.18p4 this program MUST NOT BE INSTALLED setuid root (the new smbpasswd code enforces this restriction so it cannot be run this way by accident). smbpasswd now works in a client-server mode where it contacts the local smbd to change the user's password on its behalf. This has enormous benefits - as follows. 1). smbpasswd no longer has to be setuid root - an enormous range of potential security problems is eliminated. 2). smbpasswd now has the capability to change passwords on Windows NT servers (this only works when the request is sent to the NT Primary Domain Controller if you are changing an NT Domain user's password). To run smbpasswd as a normal user just type : smbpasswd Old SMB password: New SMB Password: < type new value > Repeat New SMB Password: < re-type new value > If the old value does not match the current value stored for that user, or the two new values do not match each other, then the password will not be changed. If invoked by an ordinary user it will only allow the user to change his or her own Samba password. If run by the root user smbpasswd may take an optional argument, specifying the user name whose SMB password you wish to change. Note that when run as root smbpasswd does not prompt for or check the old password value, thus allowing root to set passwords for users who have forgotten their passwords. smbpasswd is designed to work in the same way and be familiar to UNIX users who use the passwd or yppasswd commands. For more details on using smbpasswd refer to the man page which will always be the definitive reference. Setting up Samba to support LanManager Encryption. -------------------------------------------------- This is a very brief description on how to setup samba to support password encryption. More complete instructions will probably be added later. 1) compile and install samba as usual 2) if your system can't compile the module getsmbpass.c then remove the -DSMBGETPASS define from the Makefile. 3) enable encrypted passwords in smb.conf by adding the line "encrypt passwords = yes" in the [global] section 4) create the initial smbpasswd password file in the place you specified in the Makefile. A simple way to do this based on your existing Makefile (assuming it is in a reasonably standard format) is like this: cat /etc/passwd | mksmbpasswd.sh > /usr/local/samba/private/smbpasswd Change ownership of private and smbpasswd to root. chown -R root /usr/local/samba/private Set the correct permissions on /usr/local/samba/private chmod 500 /usr/local/samba/private Set the correct permissions on /usr/local/samba/private/smbpasswd chmod 600 /usr/local/samba/private/smbpasswd note that the mksmbpasswd.sh script is in the samba source directory. If this fails then you will find that you will need entries that look like this: # SMB password file. tridge:148:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U ]:LCT-00000000:Andrew Tridgell:/home/tridge:/bin/tcsh note that the uid and username fields must be right. Also, you must get the number of X's right (there should be 32). 5) set the passwords for users using the smbpasswd command. For example, as root you could do "smbpasswd tridge" 6) try it out! Note that you can test things using smbclient, as it also now supports encryption. ============================================================================== Footnote: Please refer to WinNT.txt also ntlmaps-0.9.9.0.1/doc/NTLM Authentication Scheme for HTTP.htm0000664000076400007640000005471010252205210022533 0ustar dixonddixond NTLM Authentication Scheme for HTTP

NTLM Authentication Scheme for HTTP

Introduction

This is an attempt at documenting the undocumented NTLM authentication scheme used by M$'s browsers, proxies, and servers (MSIE and IIS); this scheme is also sometimes referred to as the NT challenge/response (NTCR) scheme. Most of the info here is derived from three sources (see also the Resources section at the end of this document): Paul Ashton's work on the NTLM security holes, the encryption documentation from Samba, and network snooping. Since most of this info is reverse-engineered it is bound to contain errors; however, at least one client and one server have been implemented according to this data and work successfully in conjunction with M$'s browsers, proxies and servers.

Note that this scheme is not as secure as Digest and some other schemes; it is slightly better than the Basic authentication scheme, however.

Also note that this scheme is not an http authentication scheme - it's a connection authentication scheme which happens to (mis-)use http status codes and headers (and even those incorrectly).

NTLM Handshake

When a client needs to authenticate itself to a proxy or server using the NTLM scheme then the following 4-way handshake takes place (only parts of the request and status line and the relevant headers are shown here; "C" is the client, "S" the server):

    1: C -> S   GET ...

    2: S -> C   401 Unauthorized
                WWW-Authenticate: NTLM

    3: C -> S   GET ...
                Authorization: NTLM <base64-encoded type-1-message>

    4: S -> C   401 Unauthorized
                WWW-Authenticate: NTLM <base64-encoded type-2-message>

    5: C -> S   GET ...
                Authorization: NTLM <base64-encoded type-3-message>

    6: S -> C   200 Ok

Messages

The three messages sent in the handshake are binary structures. Each one is described below as a pseudo-C struct and in a memory layout diagram. byte is an 8-bit field; short is a 16-bit field. All fields are unsigned. Numbers are stored in little-endian order. Struct fields named zero contain all zeroes. An array length of "*" indicates a variable length field. Hexadecimal numbers and quoted characters in the comments of the struct indicate fixed values for the given field.

The field flags is presumed to contain flags, but their significance is unknown; the values given are just those found in the packet traces.

Type-1 Message

This message contains the host name and the NT domain name of the client.

    struct {
        byte    protocol[8];     // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'
        byte    type;            // 0x01
        byte    zero[3];
        short   flags;           // 0xb203
        byte    zero[2];

        short   dom_len;         // domain string length
        short   dom_len;         // domain string length
        short   dom_off;         // domain string offset
        byte    zero[2];

        short   host_len;        // host string length
        short   host_len;        // host string length
        short   host_off;        // host string offset (always 0x20)
        byte    zero[2];

        byte    host[*];         // host string (ASCII)
        byte    dom[*];          // domain string (ASCII)
    } type-1-message
                 0       1       2       3
             +-------+-------+-------+-------+
         0:  |  'N'  |  'T'  |  'L'  |  'M'  |
             +-------+-------+-------+-------+
         4:  |  'S'  |  'S'  |  'P'  |   0   |
             +-------+-------+-------+-------+
         8:  |   1   |   0   |   0   |   0   |
             +-------+-------+-------+-------+
        12:  | 0x03  | 0xb2  |   0   |   0   |
             +-------+-------+-------+-------+
        16:  | domain length | domain length |
             +-------+-------+-------+-------+
        20:  | domain offset |   0   |   0   |
             +-------+-------+-------+-------+
        24:  |  host length  |  host length  |
             +-------+-------+-------+-------+
        28:  |  host offset  |   0   |   0   |
             +-------+-------+-------+-------+
        32:  |  host string                  |
             +                               +
             .                               .
             .                               .
             +             +-----------------+
             |             | domain string   |
             +-------------+                 +
             .                               .
             .                               .
             +-------+-------+-------+-------+
The host and domain strings are ASCII (or possibly ISO-8859-1), are uppercased, and are not nul-terminated. The host name is only the host name, not the FQDN (e.g. just "GOOFY", not "GOOFY.DISNEY.COM"). The offset's refer to the offset of the specific field within the message, and the lengths are the length of specified field. For example, in the above message host_off = 32 and dom_off = host_off + host_len. Note that the lengths are included twice (for some unfathomable reason).

Type-2 Message

This message contains the server's NTLM challenge.

    struct {
        byte    protocol[8];     // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'
        byte    type;            // 0x02
        byte    zero[7];
        short   msg_len;         // 0x28
        byte    zero[2];
        short   flags;           // 0x8201
        byte    zero[2];

        byte    nonce[8];        // nonce
        byte    zero[8];
    } type-2-message
                 0       1       2       3
             +-------+-------+-------+-------+
         0:  |  'N'  |  'T'  |  'L'  |  'M'  |
             +-------+-------+-------+-------+
         4:  |  'S'  |  'S'  |  'P'  |   0   |
             +-------+-------+-------+-------+
         8:  |   2   |   0   |   0   |   0   |
             +-------+-------+-------+-------+
        12:  |   0   |   0   |   0   |   0   |
             +-------+-------+-------+-------+
        16:  |  message len  |   0   |   0   |
             +-------+-------+-------+-------+
        20:  | 0x01  | 0x82  |   0   |   0   |
             +-------+-------+-------+-------+
        24:  |                               |
             +          server nonce         |
        28:  |                               |
             +-------+-------+-------+-------+
        32:  |   0   |   0   |   0   |   0   |
             +-------+-------+-------+-------+
        36:  |   0   |   0   |   0   |   0   |
             +-------+-------+-------+-------+
The nonce is used by the client to create the LanManager and NT responses (see Password Hashes). It is an array of 8 arbitrary bytes. The message length field contains the length of the complete message, which in this case is always 40.

Type-3 Message

This message contains the username, host name, NT domain name, and the two "responses".

    struct {
        byte    protocol[8];     // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'
        byte    type;            // 0x03
        byte    zero[3];

        short   lm_resp_len;     // LanManager response length (always 0x18)
        short   lm_resp_len;     // LanManager response length (always 0x18)
        short   lm_resp_off;     // LanManager response offset
        byte    zero[2];

        short   nt_resp_len;     // NT response length (always 0x18)
        short   nt_resp_len;     // NT response length (always 0x18)
        short   nt_resp_off;     // NT response offset
        byte    zero[2];

        short   dom_len;         // domain string length
        short   dom_len;         // domain string length
        short   dom_off;         // domain string offset (always 0x40)
        byte    zero[2];

        short   user_len;        // username string length
        short   user_len;        // username string length
        short   user_off;        // username string offset
        byte    zero[2];

        short   host_len;        // host string length
        short   host_len;        // host string length
        short   host_off;        // host string offset
        byte    zero[6];

        short   msg_len;         // message length
        byte    zero[2];

        short   flags;           // 0x8201
        byte    zero[2];

        byte    dom[*];          // domain string (unicode)
        byte    user[*];         // username string (unicode)
        byte    host[*];         // host string (unicode)
        byte    lm_resp[*];      // LanManager response
        byte    nt_resp[*];      // NT response
    } type-3-message
                 0       1       2       3
             +-------+-------+-------+-------+
         0:  |  'N'  |  'T'  |  'L'  |  'M'  |
             +-------+-------+-------+-------+
         4:  |  'S'  |  'S'  |  'P'  |   0   |
             +-------+-------+-------+-------+
         8:  |   3   |   0   |   0   |   0   |
             +-------+-------+-------+-------+
        12:  |  LM-resp len  |  LM-Resp len  |
             +-------+-------+-------+-------+
        16:  |  LM-resp off  |   0   |   0   |
             +-------+-------+-------+-------+
        20:  |  NT-resp len  |  NT-Resp len  |
             +-------+-------+-------+-------+
        24:  |  NT-resp off  |   0   |   0   |
             +-------+-------+-------+-------+
        28:  | domain length | domain length |
             +-------+-------+-------+-------+
        32:  | domain offset |   0   |   0   |
             +-------+-------+-------+-------+
        36:  |  user length  |  user length  |
             +-------+-------+-------+-------+
        40:  |  user offset  |   0   |   0   |
             +-------+-------+-------+-------+
        44:  |  host length  |  host length  |
             +-------+-------+-------+-------+
        48:  |  host offset  |   0   |   0   |
             +-------+-------+-------+-------+
        52:  |   0   |   0   |   0   |   0   |
             +-------+-------+-------+-------+
        56:  |  message len  |   0   |   0   |
             +-------+-------+-------+-------+
        60:  | 0x01  | 0x82  |   0   |   0   |
             +-------+-------+-------+-------+
        64:  | domain string                 |
             +                               +
             .                               .
             .                               .
             +           +-------------------+
             |           | user string       |
             +-----------+                   +
             .                               .
             .                               .
             +                 +-------------+
             |                 | host string |
             +-----------------+             +
             .                               .
             .                               .
             +   +---------------------------+
             |   | LanManager-response       |
             +---+                           +
             .                               .
             .                               .
             +            +------------------+
             |            | NT-response      |
             +------------+                  +
             .                               .
             .                               .
             +-------+-------+-------+-------+

The host, domain, and username strings are in Unicode (little-endian) and are not nul-terminated; the host and domain names are in upper case. The lengths of the response strings are 24.

Password Hashes

To calculate the two response strings two password hashes are used: the LanManager password hash and the NT password hash. These are described in detail in the Samba ENCRYPTION.txt document. However, a few things are not clear (such as what the magic constant for the LanManager hash is), so here is some almost-C code which calculates the two responses. Inputs are passw and nonce, the results are in lm_resp and nt_resp.

    /* setup LanManager password */

    char  lm_pw[14];
    int   len = strlen(passw);
    if (len > 14)  len = 14;

    for (idx=0; idx<len; idx++)
        lm_pw[idx] = toupper(passw[idx]);
    for (; idx<14; idx++)
        lm_pw[idx] = 0;


    /* create LanManager hashed password */

    unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
    unsigned char lm_hpw[21];
    des_key_schedule ks;

    setup_des_key(lm_pw, ks);
    des_ecb_encrypt(magic, lm_hpw, ks);

    setup_des_key(lm_pw+7, ks);
    des_ecb_encrypt(magic, lm_hpw+8, ks);

    memset(lm_hpw+16, 0, 5);


    /* create NT hashed password */

    int   len = strlen(passw);
    char  nt_pw[2*len];
    for (idx=0; idx<len; idx++)
    {
        nt_pw[2*idx]   = passw[idx];
        nt_pw[2*idx+1] = 0;
    }

    unsigned char nt_hpw[21];
    MD4_CTX context;
    MD4Init(&context);
    MD4Update(&context, nt_pw, 2*len);
    MD4Final(nt_hpw, &context);

    memset(nt_hpw+16, 0, 5);


    /* create responses */

    unsigned char lm_resp[24], nt_resp[24];
    calc_resp(lm_hpw, nonce, lm_resp);
    calc_resp(nt_hpw, nonce, nt_resp);

Helpers:

    /*
     * takes a 21 byte array and treats it as 3 56-bit DES keys. The
     * 8 byte plaintext is encrypted with each key and the resulting 24
     * bytes are stored in the results array.
     */
    void calc_resp(unsigned char *keys, unsigned char *plaintext, unsigned char *results)
    {
        des_key_schedule ks;

        setup_des_key(keys, ks);
        des_ecb_encrypt((des_cblock*) plaintext, (des_cblock*) results, ks, DES_ENCRYPT);

        setup_des_key(keys+7, ks);
        des_ecb_encrypt((des_cblock*) plaintext, (des_cblock*) (results+8), ks, DES_ENCRYPT);

        setup_des_key(keys+14, ks);
        des_ecb_encrypt((des_cblock*) plaintext, (des_cblock*) (results+16), ks, DES_ENCRYPT);
    }


    /*
     * turns a 56 bit key into the 64 bit, odd parity key and sets the key.
     * The key schedule ks is also set.
     */
    void setup_des_key(unsigned char key_56[], des_key_schedule ks)
    {
        des_cblock key;

        key[0] = key_56[0];
        key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
        key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
        key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
        key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
        key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
        key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
        key[7] =  (key_56[6] << 1) & 0xFF;

        des_set_odd_parity(&key);
        des_set_key(&key, ks);
    }

Keeping the connection alive

As mentioned above, this scheme authenticates connections, not requests. This manifests itself in that the network connection must be kept alive during the second part of the handshake, i.e. between the receiving of the type-2 message from the server (step 4) and the sending of the type-3 message (step 5). Each time the connection is closed this second part (steps 3 through 6) must be repeated over the new connection (i.e. it's not enough to just keep sending the last type-3 message). Also, once the connection is authenticated, the Authorization header need not be sent anymore while the connection stays open, no matter what resource is accessed.

For implementations wishing to work with M$'s software this means that they must make sure they use either HTTP/1.0 keep-alive's or HTTP/1.1 persistent connections, and that they must be prepared to do the second part of the handshake each time the connection was closed and is reopened. Server implementations must also make sure that HTTP/1.0 responses contain a Content-length header (as otherwise the connection must be closed after the response), and that HTTP/1.1 responses either contain a Content-length header or use the chunked transfer encoding.

Example

Here is an actual example of all the messages. Assume the host name is "LightCity", the NT domain name is "Ursa-Minor", the username is "Zaphod", the password is "Beeblebrox", and the server sends the nonce "SrvNonce". Then the handshake is:

    C -> S   GET ...

    S -> C   401 Unauthorized
             WWW-Authenticate: NTLM

    C -> S   GET ...
             Authorization: NTLM TlRMTVNTUAABAAAAA7IAAAoACgApAAAACQAJACAAAABMSUdIVENJVFlVUlNBLU1JTk9S

    S -> C   401 Unauthorized
             WWW-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABggAAU3J2Tm9uY2UAAAAAAAAAAA==

    C -> S   GET ...
             Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHIAAAAYABgAigAAABQAFABAAAAADAAMAFQAAAASABIAYAAAAAAAAACiAAAAAYIAAFUAUgBTAEEALQBNAEkATgBPAFIAWgBhAHAAaABvAGQATABJAEcASABUAEMASQBUAFkArYfKbe/jRoW5xDxHeoxC1gBmfWiS5+iX4OAN4xBKG/IFPwfH3agtPEia6YnhsADT

    S -> C   200 Ok

and the unencoded messages are:

Type-1 Message:

       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
   0:  4e 54 4c 4d 53 53 50 00 01 00 00 00 03 b2 00 00  "NTLMSSP........."
  10:  0a 00 0a 00 29 00 00 00 09 00 09 00 20 00 00 00  "....)....... ..."
  20:  4c 49 47 48 54 43 49 54 59 55 52 53 41 2d 4d 49  "LIGHTCITYURSA-MI"
  30:  4e 4f 52                                         "NOR"

Type-2 Message:

       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
   0:  4e 54 4c 4d 53 53 50 00 02 00 00 00 00 00 00 00  "NTLMSSP........."
  10:  28 00 00 00 01 82 00 00 53 72 76 4e 6f 6e 63 65  "(.......SrvNonce"
  20:  00 00 00 00 00 00 00 00                          "........"

Type-3 Message:

       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
   0:  4e 54 4c 4d 53 53 50 00 03 00 00 00 18 00 18 00  "NTLMSSP........."
  10:  72 00 00 00 18 00 18 00 8a 00 00 00 14 00 14 00  "r..............."
  20:  40 00 00 00 0c 00 0c 00 54 00 00 00 12 00 12 00  "@.......T......."
  30:  60 00 00 00 00 00 00 00 a2 00 00 00 01 82 00 00  "`..............."
  40:  55 00 52 00 53 00 41 00 2d 00 4d 00 49 00 4e 00  "U.R.S.A.-.M.I.N."
  50:  4f 00 52 00 5a 00 61 00 70 00 68 00 6f 00 64 00  "O.R.Z.a.p.h.o.d."
  60:  4c 00 49 00 47 00 48 00 54 00 43 00 49 00 54 00  "L.I.G.H.T.C.I.T."
  70:  59 00 ad 87 ca 6d ef e3 46 85 b9 c4 3c 47 7a 8c  "Y....m..F...<Gz."
  80:  42 d6 00 66 7d 68 92 e7 e8 97 e0 e0 0d e3 10 4a  "B..f}h.........J"
  90:  1b f2 05 3f 07 c7 dd a8 2d 3c 48 9a e9 89 e1 b0  "...?....-<H....."
  a0:  00 d3                                            ".."

Resources

* A tutorial on authentication schemes available on IIS
http://www.aspalliance.com/flicks/
* Encryption description for Samba
ftp://ftp.samba.org/pub/samba/docs/textdocs/ENCRYPTION.txt
* Paul Leach's presentation on NT security
http://www.cifs.com/2ndcifsconf/Microsoft-Leach3/index.html
* Similar to the above information
http://oliver.efri.hr/~crv/security/bugs/NT/ie6.html
* FAQ: NT Cryptographic Password Attacks & Defences
http://www.ntbugtraq.com/default.asp?sid=1&pid=47&aid=17
* M$'s hotfix to disable the sending of the LanManager response
ftp://ftp.microsoft.com/bussys/winnt/winnt-public/fixes/usa/NT40/hotfixes-postSP3/lm-fix
* A description of M$'s hotfix
http://www.tryc.on.ca/archives/bugtraq/1997_3/0070.html

Acknowledgements

Special thanks to the following people who helped with the collection and debugging of the above information:
Ronald Tschalär / 25. January 1999 / ronald@innovation.ch.
ntlmaps-0.9.9.0.1/lib/0000775000076400007640000000000010366274735013635 5ustar dixonddixondntlmaps-0.9.9.0.1/lib/ntlm_messages.py0000664000076400007640000003475410252205210017035 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import ntlm_procs, utils import base64, string #--------------------------------------------------------------------- class record: def __init__(self, data, offset=0): "" self.data = data self.len = len(data) self.offset = 0 self.next_offset = self.offset + self.len #--------------------------------------------------------------------- # helper function for creation info field in message 3 def create_record_info(self, offset): "" self.offset = offset len1 = utils.int2chrs(self.len) len2 = len1 data_off = utils.int2chrs(self.offset) self.record_info = len1 + len2 + data_off + '\000\000' # looks like the length is always = 8 bytes self.next_offset = offset + self.len #--------------------------------------------------------------------- def create_message1(environment_dict): "" ed = environment_dict # overall lenght = 48 bytes protocol = 'NTLMSSP\000' #name type = '\001\000' #type 1 zeros1 = '\000\000' flags = utils.hex2str(ed['FLAGS']) zeros2 = '\000\000\000\000\000\000\000\000\000' zeros3 = '\000\000\000\000\000\000\000\000\000\000\000' smthg1 = '0\000\000\000\000\000\000\000' # something with chr(48) length? smthg2 = '0\000\000\000' # something with chr(48) lenght? msg1 = protocol + type + zeros1 + flags + zeros2 + zeros3 + smthg1 + smthg2 msg1 = base64.encodestring(msg1) msg1 = string.replace(msg1, '\012', '') return msg1 #--------------------------------------------------------------------- def create_message3(nonce, environment_dict): "" ed = environment_dict flags = utils.hex2str(ed['FLAGS']) protocol = 'NTLMSSP\000' #name type = '\003\000' #type 3 head = protocol + type + '\000\000' domain_rec = record(ed['DOMAIN']) user_rec = record(ed['USER']) host_rec = record(ed['HOST']) additional_rec = record('') if ed['LM']: lm_rec = record(ntlm_procs.calc_resp(ed['LM_HASHED_PW'], nonce)) else: lm_rec = record('') if ed['NT']: nt_rec = record(ntlm_procs.calc_resp(ed['NT_HASHED_PW'], nonce)) else: nt_rec = record('') # length of the head and five infos for LM, NT, Domain, User, Host domain_offset = len(head) + 5 * 8 # and unknown record info and flags' lenght if ed['NTLM_MODE'] == 0: domain_offset = domain_offset + 8 + len(flags) # create info fields domain_rec.create_record_info(domain_offset) user_rec.create_record_info(domain_rec.next_offset) host_rec.create_record_info(user_rec.next_offset) lm_rec.create_record_info(host_rec.next_offset) nt_rec.create_record_info(lm_rec.next_offset) additional_rec.create_record_info(nt_rec.next_offset) # data part of the message 3 data_part = domain_rec.data + user_rec.data + host_rec.data + lm_rec.data + nt_rec.data # build message 3 m3 = head + lm_rec.record_info + nt_rec.record_info + domain_rec.record_info + \ user_rec.record_info + host_rec.record_info # Experimental feature !!! if ed['NTLM_MODE'] == 0: m3 = m3 + additional_rec.record_info + flags m3 = m3 + data_part # Experimental feature !!! if ed['NTLM_MODE'] == 0: m3 = m3 + additional_rec.data # base64 encode m3 = base64.encodestring(m3) m3 = string.replace(m3, '\012', '') return m3 #--------------------------------------------------------------------- def parse_message2(msg2): "" msg2 = base64.decodestring(msg2) # protocol = msg2[0:7] # msg_type = msg2[7:9] nonce = msg2[24:32] return nonce #--------------------------------------------------------------------- def item(item_str): "" item = {} res = '' item['len1'] = utils.bytes2int(item_str[0:2]) item['len2'] = utils.bytes2int(item_str[2:4]) item['offset'] = utils.bytes2int(item_str[4:6]) res = res + '%s\n\nlength (two times), offset, delimiter\n' % (utils.str2hex(item_str)) res = res + '%s decimal: %3d # length 1\n' % (utils.int2hex_str(item['len1']), item['len1']) res = res + '%s decimal: %3d # length 2\n' % (utils.int2hex_str(item['len2']), item['len2']) res = res + '%s decimal: %3d # offset\n' % (utils.int2hex_str(item['offset']), item['offset']) res = res + '%s # delimiter (two zeros)\n\n' % utils.str2hex(item_str[-2:]) item['string'] = res return item #--------------------------------------------------------------------- def flags(flag_str): "" res = '' res = res + '%s\n\n' % utils.str2hex(flag_str) flags = utils.bytes2int(flag_str[0:2]) res = res + '%s # flags\n' % (utils.int2hex_str(flags)) res = res + 'Binary:\nlayout 87654321 87654321\n' res = res + ' %s %s\n' % (utils.byte2bin_str(flag_str[1]), utils.byte2bin_str(flag_str[0])) flags2 = utils.bytes2int(flag_str[2:4]) res = res + '%s # more flags ???\n' % (utils.int2hex_str(flags2)) res = res + 'Binary:\nlayout 87654321 87654321\n' res = res + ' %s %s\n' % (utils.byte2bin_str(flag_str[3]), utils.byte2bin_str(flag_str[2])) #res = res + '%s # delimiter ???\n' % m_hex[(cur + 2) * 2: (cur + 4) * 2] return res #--------------------------------------------------------------------- def unknown_part(bin_str): "" res = '' res = res + 'Hex : %s\n' % utils.str2hex(bin_str, ' ') res = res + 'String : %s\n' % utils.str2prn_str(bin_str, ' ') res = res + 'Decimal: %s\n' % utils.str2dec(bin_str, ' ') return res #--------------------------------------------------------------------- def debug_message1(msg): "" m_ = base64.decodestring(msg) m_hex = utils.str2hex(m_) res = '' res = res + '==============================================================\n' res = res + 'NTLM Message 1 report:\n' res = res + '---------------------------------\n' res = res + 'Base64: %s\n' % msg res = res + 'String: %s\n' % utils.str2prn_str(m_) res = res + 'Hex: %s\n' % m_hex cur = 0 res = res + '---------------------------------\n' cur_len = 12 res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24]) res = res + '%s\nmethod name 0/8\n%s # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8])) res = res + '0x%s%s # message type\n' % (m_hex[18:20], m_hex[16:18]) res = res + '%s # delimiter (zeros)\n' % m_hex[20:24] cur = cur + cur_len res = res + '---------------------------------\n' cur_len = 4 res = res + 'Flags %d/%d\n' % (cur, cur_len) res = res + flags(m_[cur: cur + cur_len]) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = len(m_) - cur res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len) res = res + unknown_part(m_[cur: cur + cur_len]) res = res + '\nEnd of message 1 report.\n' return res #--------------------------------------------------------------------- def debug_message2(msg): "" m_ = base64.decodestring(msg) m_hex = utils.str2hex(m_) res = '' res = res + '==============================================================\n' res = res + 'NTLM Message 2 report:\n' res = res + '---------------------------------\n' res = res + 'Base64: %s\n' % msg res = res + 'String: %s\n' % utils.str2prn_str(m_) res = res + 'Hex: %s\n' % m_hex cur = 0 res = res + '---------------------------------\n' cur_len = 12 res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24]) res = res + '%s\nmethod name 0/8\n%s # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8])) res = res + '0x%s%s # message type\n' % (m_hex[18:20], m_hex[16:18]) res = res + '%s # delimiter (zeros)\n' % m_hex[20:24] cur = cur + cur_len res = res + '---------------------------------\n' cur_len = 8 res = res + 'Lengths and Positions %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) cur_len = 8 res = res + 'Domain ??? %d/%d\n' % (cur, cur_len) dom = item(m_[cur:cur+cur_len]) res = res + dom['string'] cur = cur + cur_len res = res + '---------------------------------\n' cur_len = 4 res = res + 'Flags %d/%d\n' % (cur, cur_len) res = res + flags(m_[cur: cur + cur_len]) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = 8 res = res + 'NONCE %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = dom['offset'] - cur res = res + 'Unknown data %d/%d:\n' % (cur, cur_len) res = res + unknown_part(m_[cur: cur + cur_len]) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = dom['len1'] res = res + 'Domain ??? %d/%d:\n' % (cur, cur_len) res = res + 'Hex: %s\n' % m_hex[cur * 2: (cur + cur_len) * 2] res = res + 'String: %s\n\n' % utils.str2prn_str(m_[cur : cur + cur_len]) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = len(m_) - cur res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len) res = res + unknown_part(m_[cur: cur + cur_len]) res = res + '\nEnd of message 2 report.\n' return res #--------------------------------------------------------------------- def debug_message3(msg): "" m_ = base64.decodestring(msg) m_hex = utils.str2hex(m_) res = '' res = res + '==============================================================\n' res = res + 'NTLM Message 3 report:\n' res = res + '---------------------------------\n' res = res + 'Base64: %s\n' % msg res = res + 'String: %s\n' % utils.str2prn_str(m_) res = res + 'Hex: %s\n' % m_hex cur = 0 res = res + '---------------------------------\n' cur_len = 12 res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24]) res = res + '%s\nmethod name 0/8\n%s # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8])) res = res + '0x%s%s # message type\n' % (m_hex[18:20], m_hex[16:18]) res = res + '%s # delimiter (zeros)\n' % m_hex[20:24] cur = cur + cur_len res = res + '---------------------------------\n' cur_len = 48 res = res + 'Lengths and Positions %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) cur_len = 8 res = res + 'LAN Manager response %d/%d\n' % (cur, cur_len) lmr = item(m_[cur:cur+cur_len]) res = res + lmr['string'] cur = cur + cur_len cur_len = 8 res = res + 'NT response %d/%d\n' % (cur, cur_len) ntr = item(m_[cur:cur+cur_len]) res = res + ntr['string'] cur = cur + cur_len cur_len = 8 res = res + 'Domain string %d/%d\n' % (cur, cur_len) dom = item(m_[cur:cur+cur_len]) res = res + dom['string'] cur = cur + cur_len cur_len = 8 res = res + 'User string %d/%d\n' % (cur, cur_len) user = item(m_[cur:cur+cur_len]) res = res + user['string'] cur = cur + cur_len cur_len = 8 res = res + 'Host string %d/%d\n' % (cur, cur_len) host = item(m_[cur:cur+cur_len]) res = res + host['string'] cur = cur + cur_len cur_len = 8 res = res + 'Unknow item record %d/%d\n' % (cur, cur_len) unknown = item(m_[cur:cur+cur_len]) res = res + unknown['string'] cur = cur + cur_len res = res + '---------------------------------\n' cur_len = 4 res = res + 'Flags %d/%d\n' % (cur, cur_len) res = res + flags(m_[cur: cur + cur_len]) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = dom['len1'] + user['len1'] + host['len1'] 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])) cur_len = dom['len1'] res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2] res = res + 'Domain name %d/%d:\n' % (cur, cur_len) res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)])) cur = cur + cur_len cur_len = user['len1'] res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2] res = res + 'User name %d/%d:\n' % (cur, cur_len) res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)])) cur = cur + cur_len cur_len = host['len1'] res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2] res = res + 'Host name %d/%d:\n' % (cur, cur_len) res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)])) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = lmr['len1'] res = res + 'LAN Manager response %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = ntr['len1'] res = res + 'NT response %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2]) cur = cur + cur_len res = res + '---------------------------------\n' cur_len = len(m_) - cur res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len) res = res + unknown_part(m_[cur: cur + cur_len]) res = res + '\nEnd of message 3 report.\n' return res ntlmaps-0.9.9.0.1/lib/ntlm_auth.py0000664000076400007640000004040110252207031016154 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import string, select, base64 import ntlm_messages, utils, ntlm_procs class ntlm_auther: """ NTLM authenticator class. Makes an HTTP authentication using NTLM method. """ #----------------------------------------------------------------------- def __init__(self): "" pass #----------------------------------------------------------------------- def proxy_ntlm_authentication(self, connection): "" connection.logger.log('*** Authorization in progress...\n') connection.close_rserver() # build an environment env = self.build_env_dict(connection) if env['NTLM_TO_BASIC']: got_credentials = self.translate_to_basic(env, connection, '407') if not got_credentials: connection.logger.log("*** Passing modified server's response to clent.\n") connection.logger.log('*** End of firts stage of NTLM translation.\n') return connection.connect_rserver() NTLM_msg1 = ntlm_messages.create_message1(env) connection.logger_auth.log(ntlm_messages.debug_message1(NTLM_msg1)) tmp_client_head_obj = connection.client_head_obj.copy() tmp_client_head_obj.replace_param_value('Proxy-Connection', 'Keep-Alive') tmp_client_head_obj.replace_param_value('Proxy-Authorization', 'NTLM ' + NTLM_msg1) connection.reset_rserver() connection.rserver_buffer = '' connection.logger.log('*** Remote server buffer flushed.\n') # If we are POST/PUT-ing a large chunk of data we don't want # to do this at this time, so we change the data to 'abc' with # lenght = 3. if connection.client_head_obj.get_http_method() in ('POST', 'PUT'): tmp_client_head_obj.replace_param_value('Content-Length', '3') connection.logger.log('*** Fake NTLM header with Msg1:\n=====\n' + tmp_client_head_obj.__repr__()) connection.logger.log('*** Sending Fake NTLM header with Msg1...') tmp_client_head_obj.send(connection.rserver_socket) connection.logger.log('Done.\n') if connection.client_head_obj.get_http_method() in ('POST', 'PUT'): try: connection.logger.log("*** Sending fake 'abc' bytes body...") connection.rserver_socket.send('abc') connection.logger.log("Done.\n") except: # could not send data to remote server and have to end function connection.rserver_socket_closed = 1 connection.logger.log('Failed.\n*** Could not send client data to remote server. Exception in send().\n') return else: connection.logger.log("*** There must be no body to send.\n") connection.logger.log('*** Waiting for message 2 from remote server...\n') while((not connection.rserver_all_got) and (not connection.rserver_socket_closed)): select.select([connection.rserver_socket.fileno()], [], [], 2.0) connection.run_rserver_loop() if connection.config['DEBUG']['SCR_DEBUG']: print '\b+', if connection.rserver_head_obj: connection.logger.log('*** Got NTLM message 2 from remote server.\n') else: # could not get response with msg2 from remote server and have to end function connection.logger.log('*** Could not get response with msg2 from remote server.\n') connection.logger.log('*** Stop Request = %d.\n' % connection.stop_request) return auth = connection.rserver_head_obj.get_param_values('Proxy-Authenticate') if auth: msg2 = string.strip(string.split(auth[0])[1]) connection.logger_auth.log(ntlm_messages.debug_message2(msg2)) nonce = ntlm_messages.parse_message2(msg2) NTLM_msg3 = ntlm_messages.create_message3(nonce, env) connection.logger_auth.log(ntlm_messages.debug_message3(NTLM_msg3)) else: NTLM_msg3 = '' tmp_client_head_obj = connection.client_head_obj.copy() tmp_client_head_obj.replace_param_value('Proxy-Authorization', 'NTLM ' + NTLM_msg3) connection.reset_rserver() connection.rserver_buffer = '' connection.logger.log('*** Remote server buffer flushed.\n') connection.logger.log('*** Sending Fake NTLM header (not body) with Msg3...') tmp_client_head_obj.send(connection.rserver_socket) connection.logger.log('Done.\n') connection.logger.log('*** Fake NTLM header with Msg3:\n=====\n' + tmp_client_head_obj.__repr__()) # upon exit all the remote server variables are reset # so new remote server response will be taken by the usual way in connection.run() connection.logger.log('*** End of NTLM authorization process.\n') #----------------------------------------------------------------------- def www_ntlm_authentication(self, connection): "" connection.logger.log('*** Authorization in progress...\n') connection.close_rserver() # build an environment env = self.build_env_dict(connection) if env['NTLM_TO_BASIC']: got_credentials = self.translate_to_basic(env, connection, '401') if not got_credentials: connection.logger.log("*** Passing modified server's response to clent.\n") connection.logger.log('*** End of firts stage of NTLM translation.\n') return connection.connect_rserver() NTLM_msg1 = ntlm_messages.create_message1(env) connection.logger_auth.log(ntlm_messages.debug_message1(NTLM_msg1)) tmp_client_head_obj = connection.client_head_obj.copy() tmp_client_head_obj.replace_param_value('Connection', 'Keep-Alive') #tmp_client_head_obj.replace_param_value('Authorization', 'Negotiate ' + NTLM_msg1) tmp_client_head_obj.replace_param_value('Authorization', 'NTLM ' + NTLM_msg1) connection.reset_rserver() connection.rserver_buffer = '' connection.logger.log('*** Remote server buffer flushed.\n') # If we are POST/PUT-ing a large chunk of data we don't want # to do this at this time, so we change the data to 'abc' with # lenght = 3. if connection.client_head_obj.get_http_method() in ('POST', 'PUT'): tmp_client_head_obj.replace_param_value('Content-Length', '3') connection.logger.log('*** Fake NTLM header with Msg1:\n=====\n' + tmp_client_head_obj.__repr__()) connection.logger.log('*** Sending Fake NTLM header (and body) with Msg1...') tmp_client_head_obj.send(connection.rserver_socket) if connection.client_head_obj.get_http_method() in ('POST', 'PUT'): try: connection.rserver_socket.send('abc') except: # could not send data to remote server and have to end function connection.rserver_socket_closed = 1 connection.logger.log('Failed.\n*** Could not send client data to remote server. Exception in send().\n') return connection.logger.log('Done.\n') connection.logger.log('*** Waiting for message 2 from the remote server...\n') while((not connection.rserver_all_got) and (not connection.rserver_socket_closed)): select.select([connection.rserver_socket.fileno()], [], [], 2.0) connection.run_rserver_loop() if connection.config['DEBUG']['SCR_DEBUG']: print '\b+', if connection.rserver_head_obj: connection.logger.log('*** Got NTLM message 2 from server.\n') # connection.logger.log('*** Remote server header with NTLM Msg2:\n=====\n' + connection.rserver_head_obj.__repr__()) else: # could not get response with msg2 from remote server and have to end function connection.logger.log('*** Could not get response with msg2 from server.\n') connection.logger.log('*** Stop Request = %d.\n' % connection.stop_request) return auth = connection.rserver_head_obj.get_param_values('Www-Authenticate') if auth: #connection.logger.log('### %s\n' % auth) msg2 = string.strip(string.split(auth[0])[1]) connection.logger_auth.log(ntlm_messages.debug_message2(msg2)) nonce = ntlm_messages.parse_message2(msg2) NTLM_msg3 = ntlm_messages.create_message3(nonce, env) connection.logger_auth.log(ntlm_messages.debug_message3(NTLM_msg3)) else: NTLM_msg3 = '' tmp_client_head_obj = connection.client_head_obj.copy() #tmp_client_head_obj.replace_param_value('Authorization', 'Negotiate ' + NTLM_msg3) tmp_client_head_obj.replace_param_value('Authorization', 'NTLM ' + NTLM_msg3) connection.reset_rserver() connection.rserver_buffer = '' connection.logger.log('*** Remote server buffer flushed.\n') connection.logger.log('*** Sending Fake NTLM header (not body) with Msg3...') tmp_client_head_obj.send(connection.rserver_socket) connection.logger.log('Done.\n') connection.logger.log('*** Fake NTLM header with Msg3:\n=====\n' + tmp_client_head_obj.__repr__()) # upon exit all the remote server variables are reset # so new remote server response will be taken by the usual way in connection.run() connection.logger.log('*** End of NTLM authorization process.\n') #----------------------------------------------------------------------- def build_env_dict(self, connection): "" connection.logger.log('*** Building environment for NTLM.\n') env = {} if connection.config['NTLM_AUTH']['NTLM_FLAGS']: env['FLAGS'] = connection.config['NTLM_AUTH']['NTLM_FLAGS'] connection.logger.log('*** Using custom NTLM flags: %s\n' % env['FLAGS']) else: # I have seen flag field '\005\202' as well (with NT response). #0x8206 or 0x8207 or 0x8205 env['FLAGS'] = "06820000" #flags = utils.hex2str(ed['NTLM_FLAGS']) connection.logger.log('*** Using default NTLM flags: %s\n' % env['FLAGS']) env['LM'] = connection.config['NTLM_AUTH']['LM_PART'] env['NT'] = connection.config['NTLM_AUTH']['NT_PART'] # we must have at least LM part if not (env['LM'] or env['NT']): env['LM'] = 1 if env['LM'] == 1 and env['NT'] == 0: connection.logger.log('*** NTLM version with LM response only.\n') elif env['LM'] == 1 and env['NT'] == 1: connection.logger.log('*** NTLM version with LM and NT responses.\n') elif env['LM'] == 0 and env['NT'] == 1: connection.logger.log('*** NTLM version with NT response only.\n') #env['UNICODE'] = connection.config['NTLM_AUTH']['UNICODE'] if env['NT']: env['UNICODE'] = 1 else: env['UNICODE'] = 0 # have to put these ones into [NTLM] section env['DOMAIN'] = string.upper(connection.config['NTLM_AUTH']['NT_DOMAIN']) # Check if there is explicit NT_Hostname in config, if there is one then take it, # if there is no one then take gethostname() result. if connection.config['NTLM_AUTH']['NT_HOSTNAME']: env['HOST'] = string.upper(connection.config['NTLM_AUTH']['NT_HOSTNAME']) else: env['HOST'] = string.upper(connection.config['GENERAL']['HOST']) env['USER'] = string.upper(connection.config['NTLM_AUTH']['USER']) connection.logger.log('*** NTLM Domain/Host/User: %s/%s/%s\n' % (env['DOMAIN'], env['HOST'], env['USER'])) # have to use UNICODE stings if env['UNICODE']: env['DOMAIN'] = utils.str2unicode(env['DOMAIN']) env['HOST'] = utils.str2unicode(env['HOST']) env['USER'] = utils.str2unicode(env['USER']) connection.logger.log('*** Using UNICODE stings.\n') if connection.config['NTLM_AUTH']['LM_HASHED_PW'] and connection.config['NTLM_AUTH']['NT_HASHED_PW']: env['LM_HASHED_PW'] = connection.config['NTLM_AUTH']['LM_HASHED_PW'] env['NT_HASHED_PW'] = connection.config['NTLM_AUTH']['NT_HASHED_PW'] connection.logger.log('*** NTLM hashed passwords found.\n') # Test params if connection.config['NTLM_AUTH'].has_key('NTLM_MODE'): env['NTLM_MODE'] = int(connection.config['NTLM_AUTH']['NTLM_MODE']) else: env['NTLM_MODE'] = 0 # End of test params env['NTLM_TO_BASIC'] = connection.config['NTLM_AUTH']['NTLM_TO_BASIC'] connection.logger.log('*** Environment has been built successfully.\n') return env #----------------------------------------------------------------------- def translate_to_basic(self, environment, connection, error_code): "" connection.logger.log('*** Translating NTLM to Basic...\n') user, password = self.get_credentials_from_basic(connection, error_code) if user: connection.logger.log("*** Found Basic credentials in client's header.\n") environment['USER'] = user #environment['PASSWORD'] = password connection.logger.log("*** Basic User/Password: %s/%s.\n" % (user, password)) connection.logger.log("*** Calculating hashed passwords (LM and NT)...") environment['LM_HASHED_PW'] = ntlm_procs.create_LM_hashed_password(password) environment['NT_HASHED_PW'] = ntlm_procs.create_NT_hashed_password(password) connection.logger.log("Done.\n") return 1 else: connection.logger.log("*** There are no basic credentials in client's header.\n") connection.logger.log("*** Replacing NTLM value with Basic in rserver's header...") self.replace_ntlm_with_basic(connection, error_code) connection.logger.log("Done.\n") connection.logger.log("*** New server's header:\n=====\n" + connection.rserver_head_obj.__repr__()) return 0 #----------------------------------------------------------------------- def replace_ntlm_with_basic(self, connection, error_code): "" if error_code == '401': value_name = 'Www-Authenticate' else: value_name = 'Proxy-Authenticate' realm = connection.client_head_obj.get_http_server() basic_str = 'Basic realm="%s:%s"' % realm connection.rserver_head_obj.replace_param_value(value_name, basic_str) #----------------------------------------------------------------------- def get_credentials_from_basic(self, connection, error_code): "" if error_code == '401': value_name = 'Authorization' else: value_name = 'Proxy-Authorization' l = connection.client_head_obj.get_param_values(value_name) user, password = '', '' for i in l: t = string.split(i)[0] if string.lower(t) == 'basic': b64 = string.split(i)[1] cred = base64.decodestring(b64) user = string.split(cred, ':')[0] password = string.split(cred, ':')[1] return user, password ntlmaps-0.9.9.0.1/lib/utils.py0000664000076400007640000000775110252205210015331 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import string hd = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',] #-------------------------------------------------------------------------------------------- def str2hex_num(str): res = 0L for i in str: res = res << 8 res = res + long(ord(i)) return hex(res) #-------------------------------------------------------------------------------------------- def str2hex(str, delimiter=''): res = '' for i in str: res = res + hd[ord(i)/16] res = res + hd[ord(i) - ((ord(i)/16) * 16)] res = res + delimiter return res #-------------------------------------------------------------------------------------------- def str2dec(str, delimiter=''): res = '' for i in str: res = res + '%3d' % ord(i) res = res + delimiter return res #-------------------------------------------------------------------------------------------- def hex2str(hex_str): res = '' for i in range(0, len(hex_str), 2): res = res + (chr(hd.index(hex_str[i]) * 16 + hd.index(hex_str[i+1]))) return res #-------------------------------------------------------------------------------------------- def str2prn_str(bin_str, delimiter=''): "" res = '' for i in bin_str: if ord(i) > 31: res = res + i else: res = res + '.' res = res + delimiter return res #-------------------------------------------------------------------------------------------- def byte2bin_str(char): "" res = '' t = ord(char) while t > 0: t1 = t / 2 if t != 2 * t1: res = '1' + res else: res = '0' + res t = t1 if len(res) < 8: res = '0' * (8 - len(res)) + res return res #-------------------------------------------------------------------------------------------- def str2lst(str): res = [] for i in str: res.append(ord(i)) return res #-------------------------------------------------------------------------------------------- def lst2str(lst): res = '' for i in lst: res = res + chr(i & 0xFF) return res #-------------------------------------------------------------------------------------------- def int2chrs(number_int): "" return chr(number_int & 0xFF) + chr((number_int >> 8) & 0xFF) #-------------------------------------------------------------------------------------------- def bytes2int(bytes): "" return ord(bytes[1]) * 256 + ord(bytes[0]) #-------------------------------------------------------------------------------------------- def int2hex_str(number_int16): "" res = '0x' ph = int(number_int16) / 256 res = res + hd[ph/16] res = res + hd[ph - ((ph/16) * 16)] pl = int(number_int16) - (ph * 256) res = res + hd[pl/16] res = res + hd[pl - ((pl/16) * 16)] return res #-------------------------------------------------------------------------------------------- def str2unicode(string): "converts ascii string to dumb unicode" res = '' for i in string: res = res + i + '\000' return res ntlmaps-0.9.9.0.1/lib/U32.py0000664000076400007640000000572010252205210014534 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # C = 0x1000000000L def norm(n): return n & 0xFFFFFFFFL class U32: v = 0L def __init__(self, value = 0): self.v = C + norm(abs(long(value))) def set(self, value = 0): self.v = C + norm(abs(long(value))) def __repr__(self): return hex(norm(self.v)) def __long__(self): return long(norm(self.v)) def __int__(self): return int(norm(self.v)) def __chr__(self): return chr(norm(self.v)) def __add__(self, b): r = U32() r.v = C + norm(self.v + b.v) return r def __sub__(self, b): r = U32() if self.v < b.v: r.v = C + norm(0x100000000L - (b.v - self.v)) else: r.v = C + norm(self.v - b.v) return r def __mul__(self, b): r = U32() r.v = C + norm(self.v * b.v) return r def __div__(self, b): r = U32() r.v = C + (norm(self.v) / norm(b.v)) return r def __mod__(self, b): r = U32() r.v = C + (norm(self.v) % norm(b.v)) return r def __neg__(self): return U32(self.v) def __pos__(self): return U32(self.v) def __abs__(self): return U32(self.v) def __invert__(self): r = U32() r.v = C + norm(~self.v) return r def __lshift__(self, b): r = U32() r.v = C + norm(self.v << b) return r def __rshift__(self, b): r = U32() r.v = C + (norm(self.v) >> b) return r def __and__(self, b): r = U32() r.v = C + norm(self.v & b.v) return r def __or__(self, b): r = U32() r.v = C + norm(self.v | b.v) return r def __xor__(self, b): r = U32() r.v = C + norm(self.v ^ b.v) return r def __not__(self): return U32(not norm(self.v)) def truth(self): return norm(self.v) def __cmp__(self, b): if norm(self.v) > norm(b.v): return 1 elif norm(self.v) < norm(b.v): return -1 else: return 0 def __nonzero__(self): return norm(self.v)ntlmaps-0.9.9.0.1/lib/config_affairs.py0000664000076400007640000001113010366274127017136 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import socket, thread, string, sys import logger #------------------------------------------------------------------------- def arrange(conf): "" #----------------------------------------------- # GENERAL conf['GENERAL']['PARENT_PROXY'] # if we do not use proxy then we do not need its port if conf['GENERAL']['PARENT_PROXY']: conf['GENERAL']['AVAILABLE_PROXY_LIST'] = string.split(conf['GENERAL']['PARENT_PROXY']) conf['GENERAL']['PARENT_PROXY'] = conf['GENERAL']['AVAILABLE_PROXY_LIST'].pop() conf['GENERAL']['PARENT_PROXY_PORT'] = makeInt(conf['GENERAL']['PARENT_PROXY_PORT'], 'PARENT_PROXY_PORT') conf['GENERAL']['PARENT_PROXY_TIMEOUT'] = makeInt(conf['GENERAL']['PARENT_PROXY_TIMEOUT'], 'PARENT_PROXY_TIMEOUT') try: conf['GENERAL']['MAX_CONNECTION_BACKLOG'] = int(conf['GENERAL']['MAX_CONNECTION_BACKLOG']) except ValueError: if conf['GENERAL']['MAX_CONNECTION_BACKLOG'] == 'SOMAXCONN': conf['GENERAL']['MAX_CONNECTION_BACKLOG'] = socket.SOMAXCONN else: print "ERROR: There is a problem with 'MAX_CONNECTION_BACKLOG' in the config (neither a number nor 'SOMAXCONN'?)" sys.exit(1) conf['GENERAL']['LISTEN_PORT'] = makeInt(conf['GENERAL']['LISTEN_PORT'], 'LISTEN_PORT') conf['GENERAL']['ALLOW_EXTERNAL_CLIENTS'] = makeInt(conf['GENERAL']['ALLOW_EXTERNAL_CLIENTS'], 'ALLOW_EXTERNAL_CLIENTS') hostname = socket.gethostname() conf['GENERAL']['HOST'] = hostname try: externalIP = socket.gethostbyname_ex(hostname)[2] except socket.error: # socket.gaierror in Python 2.x 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." externalIP = [] conf['GENERAL']['HOST_IP_LIST'] = externalIP + ['127.0.0.1'] conf['GENERAL']['FRIENDLY_IPS'] = conf['GENERAL']['HOST_IP_LIST'] + string.split(conf['GENERAL']['FRIENDLY_IPS']) conf['GENERAL']['URL_LOG'] = makeInt(conf['GENERAL']['URL_LOG'], 'URL_LOG') url_logger = logger.Logger('url.log', conf['GENERAL']['URL_LOG']) url_logger_lock = thread.allocate_lock() conf['GENERAL']['URL_LOGGER'] = url_logger conf['GENERAL']['URL_LOG_LOCK'] = url_logger_lock #----------------------------------------------- # NTLM_AUTH if not conf['NTLM_AUTH'].has_key('NTLM_FLAGS'): conf['NTLM_AUTH']['NTLM_FLAGS'] = '' #conf['NTLM']['FULL_NTLM'] = makeInt(conf['NTLM']['FULL_NTLM'], 'FULL_NTLM') conf['NTLM_AUTH']['LM_PART'] = makeInt(conf['NTLM_AUTH']['LM_PART'], 'LM_PART') conf['NTLM_AUTH']['NT_PART'] = makeInt(conf['NTLM_AUTH']['NT_PART'], 'NT_PART') conf['NTLM_AUTH']['NTLM_TO_BASIC'] = makeInt(conf['NTLM_AUTH']['NTLM_TO_BASIC'], 'NTLM_TO_BASIC') if not conf['NTLM_AUTH']['NT_DOMAIN']: print "ERROR: NT DOMAIN must be set." sys.exit(1) if not conf['NTLM_AUTH'].has_key('PASSWORD'): conf['NTLM_AUTH']['PASSWORD'] = '' #----------------------------------------------- # DEBUG conf['DEBUG']['DEBUG'] = makeInt(conf['DEBUG']['DEBUG'], 'DEBUG') conf['DEBUG']['AUTH_DEBUG'] = makeInt(conf['DEBUG']['AUTH_DEBUG'], 'AUTH_DEBUG') conf['DEBUG']['BIN_DEBUG'] = makeInt(conf['DEBUG']['BIN_DEBUG'], 'BIN_DEBUG') # screen activity if conf['DEBUG'].has_key('SCR_DEBUG'): conf['DEBUG']['SCR_DEBUG'] = int(conf['DEBUG']['SCR_DEBUG']) else: conf['DEBUG']['SCR_DEBUG'] = 0 return conf def makeInt(string, errorDesc='an item'): try: ret = int(string) except ValueError: print "ERROR: There is a problem with "+errorDesc+" in the config (is it not a number?)" sys.exit(1) return ret ntlmaps-0.9.9.0.1/lib/monitor_upstream.py0000664000076400007640000001311510252207321017574 0ustar dixonddixond# This file is Copyright 2004 Darryl A. Dixon # and is part of 'NTLM Authorization Proxy Server', # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import signal, httplib, time, socket, thread, os #-------------------------------------------------------------- class monitorThread: #-------------------------------------------------------------- def __init__(self, config, die_sig=signal.SIGINT): self.alive = 1 self.config = config self.die_sig = die_sig self.threadsToKill = [] self.timeoutSeconds = self.config['GENERAL']['PARENT_PROXY_TIMEOUT'] #-------------------------------------------------------------- def run(self): while self.alive: self.alarmThread = timerThread(self.timeoutSeconds, self.alive, self.die_sig) thread.start_new_thread(self.alarmThread.run, ()) # We poll the current proxy for responsiveness... # TODO: add logger entries for all these exceptions self.httpTest() self.alarmThread.alive = 0 time.sleep(self.timeoutSeconds+1) # Maximum timeout before hitting bottom of this loop is therefore # at most self.timeoutSeconds*2+1 and at least self.timeoutSeconds+1. # Hopefully this is a reasonable tradeoff between noticeable # service outage for user and creaming the proxy with stacks of # our spurious requests... :) def die(self): try: self.alarmThread.alive = 0 except AttributeError: pass # self.alarmThread is already dead self.alive = 0 die_func = signal.getsignal(self.die_sig) die_func(self.die_sig) def httpTest(self): """ Although the httplib.HTTP class is retained in Python 2.x for backwards compatability with Python 1.5 it is not recommended for new code, and as most users will now be using the Python 2.x stream I have abstracted away two code paths in here. The maintenance will be slightly higher, but in the long term it will make it easier to fork off a Python 1.5.2 maintenance version of ntlmaps and just keep the pure Python 2.x optimised version. """ if 'HTTPConnection' in dir(httplib): # Python 2.x try: conn = httplib.HTTPConnection(self.config['GENERAL']['PARENT_PROXY'], self.config['GENERAL']['PARENT_PROXY_PORT']) try: conn.request("GET", "/") try: data = conn.getresponse() try: if not data.read(): # Got a b0rked response? self.die() except AssertionError: # Yup, got a wacky response self.die() conn.close() except (AttributeError, httplib.BadStatusLine): # Didn't somehow connect? self.die() except socket.error: # Service not running/listening on specified port? self.die() except socket.gaierror: # Name resolution error for this proxy? self.die() else: # Python 1.5.2 try: # This call fails far more regularly than it should. Best to move to # using Python 2.x conn = httplib.HTTP(self.config['GENERAL']['PARENT_PROXY'], self.config['GENERAL']['PARENT_PROXY_PORT']) try: conn.putrequest('GET', '/') conn.endheaders() try: errcode, errmsg, headers = conn.getreply() try: data = conn.getfile() if not data.read(): self.die() data.close() except: self.die() except: self.die() except: self.die() except: self.die() return #-------------------------------------------------------------- class timerThread: """ Used in place of SIGALRM as Windows doesn't support it. """ #-------------------------------------------------------------- def __init__(self, seconds, parentAlive, die_sig=signal.SIGINT): self.seconds = seconds self.parentAlive = parentAlive self.die_sig = die_sig self.alive = 1 #-------------------------------------------------------------- def run(self): time.sleep(self.seconds) if self.alive and self.parentAlive: die_func = signal.getsignal(self.die_sig) die_func(self.die_sig) ntlmaps-0.9.9.0.1/lib/basic_auth.py0000664000076400007640000000731210252205210016264 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import base64 class basic_auther: #----------------------------------------------------------------------- def __init__(self): "" pass #----------------------------------------------------------------------- def build_credentials(config_dic): "" msg = config_dic['USER'] + ":" + config_dic['PASSWORD'] msg = base64.encodestring(msg) msg = string.replace(msg3, '\012', '') return msg #----------------------------------------------------------------------- def proxy_basic_authentication(self, connection): "" connection.logger.log('*** Basic authorization in progress...\n') connection.close_rserver() connection.connect_rserver() connection.reset_rserver() connection.rserver_buffer = '' connection.logger.log('*** Remote server buffer flushed.\n') basic_string = self.build_credentials(connection.config['GENERAL']) tmp_client_head_obj = connection.client_head_obj.copy() tmp_client_head_obj.replace_param_value('Proxy-Authorization', 'Basic ' + basic_sting) connection.logger.log('*** Sending client header (not body) with Basic auth...') tmp_client_head_obj.send(connection.rserver_socket) connection.logger.log('Done.\n') connection.logger.log('*** New client header with Basic auth:\n=====\n' + tmp_client_head_obj.__repr__()) # upon exit all the remote server variables are reset # so new remote server response will be taken by the usual way in connection.run() connection.logger.log('*** End of Basic authorization process.\n') #----------------------------------------------------------------------- def www_basic_authentication(self, connection): "" connection.logger.log('*** Basic authorization in progress...\n') connection.close_rserver() connection.connect_rserver() connection.reset_rserver() connection.rserver_buffer = '' connection.logger.log('*** Remote server buffer flushed.\n') basic_string = self.build_credentials(connection.config['GENERAL']) tmp_client_head_obj = connection.client_head_obj.copy() tmp_client_head_obj.replace_param_value('Authorization', 'Basic ' + basic_sting) connection.logger.log('*** Sending client header (not body) with Basic auth...') tmp_client_head_obj.send(connection.rserver_socket) connection.logger.log('Done.\n') connection.logger.log('*** New client header with Basic auth:\n=====\n' + tmp_client_head_obj.__repr__()) # upon exit all the remote server variables are reset # so new remote server response will be taken by the usual way in connection.run() connection.logger.log('*** End of Basic authorization process.\n') ntlmaps-0.9.9.0.1/lib/des_data.py0000664000076400007640000004701210252205210015727 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # from U32 import U32 # static unsigned long des_SPtrans[8][64]={ des_SPtrans =\ [ #nibble 0 [ U32(0x00820200L), U32(0x00020000L), U32(0x80800000L), U32(0x80820200L), U32(0x00800000L), U32(0x80020200L), U32(0x80020000L), U32(0x80800000L), U32(0x80020200L), U32(0x00820200L), U32(0x00820000L), U32(0x80000200L), U32(0x80800200L), U32(0x00800000L), U32(0x00000000L), U32(0x80020000L), U32(0x00020000L), U32(0x80000000L), U32(0x00800200L), U32(0x00020200L), U32(0x80820200L), U32(0x00820000L), U32(0x80000200L), U32(0x00800200L), U32(0x80000000L), U32(0x00000200L), U32(0x00020200L), U32(0x80820000L), U32(0x00000200L), U32(0x80800200L), U32(0x80820000L), U32(0x00000000L), U32(0x00000000L), U32(0x80820200L), U32(0x00800200L), U32(0x80020000L), U32(0x00820200L), U32(0x00020000L), U32(0x80000200L), U32(0x00800200L), U32(0x80820000L), U32(0x00000200L), U32(0x00020200L), U32(0x80800000L), U32(0x80020200L), U32(0x80000000L), U32(0x80800000L), U32(0x00820000L), U32(0x80820200L), U32(0x00020200L), U32(0x00820000L), U32(0x80800200L), U32(0x00800000L), U32(0x80000200L), U32(0x80020000L), U32(0x00000000L), U32(0x00020000L), U32(0x00800000L), U32(0x80800200L), U32(0x00820200L), U32(0x80000000L), U32(0x80820000L), U32(0x00000200L), U32(0x80020200L), ], #nibble 1 [ U32(0x10042004L), U32(0x00000000L), U32(0x00042000L), U32(0x10040000L), U32(0x10000004L), U32(0x00002004L), U32(0x10002000L), U32(0x00042000L), U32(0x00002000L), U32(0x10040004L), U32(0x00000004L), U32(0x10002000L), U32(0x00040004L), U32(0x10042000L), U32(0x10040000L), U32(0x00000004L), U32(0x00040000L), U32(0x10002004L), U32(0x10040004L), U32(0x00002000L), U32(0x00042004L), U32(0x10000000L), U32(0x00000000L), U32(0x00040004L), U32(0x10002004L), U32(0x00042004L), U32(0x10042000L), U32(0x10000004L), U32(0x10000000L), U32(0x00040000L), U32(0x00002004L), U32(0x10042004L), U32(0x00040004L), U32(0x10042000L), U32(0x10002000L), U32(0x00042004L), U32(0x10042004L), U32(0x00040004L), U32(0x10000004L), U32(0x00000000L), U32(0x10000000L), U32(0x00002004L), U32(0x00040000L), U32(0x10040004L), U32(0x00002000L), U32(0x10000000L), U32(0x00042004L), U32(0x10002004L), U32(0x10042000L), U32(0x00002000L), U32(0x00000000L), U32(0x10000004L), U32(0x00000004L), U32(0x10042004L), U32(0x00042000L), U32(0x10040000L), U32(0x10040004L), U32(0x00040000L), U32(0x00002004L), U32(0x10002000L), U32(0x10002004L), U32(0x00000004L), U32(0x10040000L), U32(0x00042000L), ], #nibble 2 [ U32(0x41000000L), U32(0x01010040L), U32(0x00000040L), U32(0x41000040L), U32(0x40010000L), U32(0x01000000L), U32(0x41000040L), U32(0x00010040L), U32(0x01000040L), U32(0x00010000L), U32(0x01010000L), U32(0x40000000L), U32(0x41010040L), U32(0x40000040L), U32(0x40000000L), U32(0x41010000L), U32(0x00000000L), U32(0x40010000L), U32(0x01010040L), U32(0x00000040L), U32(0x40000040L), U32(0x41010040L), U32(0x00010000L), U32(0x41000000L), U32(0x41010000L), U32(0x01000040L), U32(0x40010040L), U32(0x01010000L), U32(0x00010040L), U32(0x00000000L), U32(0x01000000L), U32(0x40010040L), U32(0x01010040L), U32(0x00000040L), U32(0x40000000L), U32(0x00010000L), U32(0x40000040L), U32(0x40010000L), U32(0x01010000L), U32(0x41000040L), U32(0x00000000L), U32(0x01010040L), U32(0x00010040L), U32(0x41010000L), U32(0x40010000L), U32(0x01000000L), U32(0x41010040L), U32(0x40000000L), U32(0x40010040L), U32(0x41000000L), U32(0x01000000L), U32(0x41010040L), U32(0x00010000L), U32(0x01000040L), U32(0x41000040L), U32(0x00010040L), U32(0x01000040L), U32(0x00000000L), U32(0x41010000L), U32(0x40000040L), U32(0x41000000L), U32(0x40010040L), U32(0x00000040L), U32(0x01010000L), ], #nibble 3 [ U32(0x00100402L), U32(0x04000400L), U32(0x00000002L), U32(0x04100402L), U32(0x00000000L), U32(0x04100000L), U32(0x04000402L), U32(0x00100002L), U32(0x04100400L), U32(0x04000002L), U32(0x04000000L), U32(0x00000402L), U32(0x04000002L), U32(0x00100402L), U32(0x00100000L), U32(0x04000000L), U32(0x04100002L), U32(0x00100400L), U32(0x00000400L), U32(0x00000002L), U32(0x00100400L), U32(0x04000402L), U32(0x04100000L), U32(0x00000400L), U32(0x00000402L), U32(0x00000000L), U32(0x00100002L), U32(0x04100400L), U32(0x04000400L), U32(0x04100002L), U32(0x04100402L), U32(0x00100000L), U32(0x04100002L), U32(0x00000402L), U32(0x00100000L), U32(0x04000002L), U32(0x00100400L), U32(0x04000400L), U32(0x00000002L), U32(0x04100000L), U32(0x04000402L), U32(0x00000000L), U32(0x00000400L), U32(0x00100002L), U32(0x00000000L), U32(0x04100002L), U32(0x04100400L), U32(0x00000400L), U32(0x04000000L), U32(0x04100402L), U32(0x00100402L), U32(0x00100000L), U32(0x04100402L), U32(0x00000002L), U32(0x04000400L), U32(0x00100402L), U32(0x00100002L), U32(0x00100400L), U32(0x04100000L), U32(0x04000402L), U32(0x00000402L), U32(0x04000000L), U32(0x04000002L), U32(0x04100400L), ], #nibble 4 [ U32(0x02000000L), U32(0x00004000L), U32(0x00000100L), U32(0x02004108L), U32(0x02004008L), U32(0x02000100L), U32(0x00004108L), U32(0x02004000L), U32(0x00004000L), U32(0x00000008L), U32(0x02000008L), U32(0x00004100L), U32(0x02000108L), U32(0x02004008L), U32(0x02004100L), U32(0x00000000L), U32(0x00004100L), U32(0x02000000L), U32(0x00004008L), U32(0x00000108L), U32(0x02000100L), U32(0x00004108L), U32(0x00000000L), U32(0x02000008L), U32(0x00000008L), U32(0x02000108L), U32(0x02004108L), U32(0x00004008L), U32(0x02004000L), U32(0x00000100L), U32(0x00000108L), U32(0x02004100L), U32(0x02004100L), U32(0x02000108L), U32(0x00004008L), U32(0x02004000L), U32(0x00004000L), U32(0x00000008L), U32(0x02000008L), U32(0x02000100L), U32(0x02000000L), U32(0x00004100L), U32(0x02004108L), U32(0x00000000L), U32(0x00004108L), U32(0x02000000L), U32(0x00000100L), U32(0x00004008L), U32(0x02000108L), U32(0x00000100L), U32(0x00000000L), U32(0x02004108L), U32(0x02004008L), U32(0x02004100L), U32(0x00000108L), U32(0x00004000L), U32(0x00004100L), U32(0x02004008L), U32(0x02000100L), U32(0x00000108L), U32(0x00000008L), U32(0x00004108L), U32(0x02004000L), U32(0x02000008L), ], #nibble 5 [ U32(0x20000010L), U32(0x00080010L), U32(0x00000000L), U32(0x20080800L), U32(0x00080010L), U32(0x00000800L), U32(0x20000810L), U32(0x00080000L), U32(0x00000810L), U32(0x20080810L), U32(0x00080800L), U32(0x20000000L), U32(0x20000800L), U32(0x20000010L), U32(0x20080000L), U32(0x00080810L), U32(0x00080000L), U32(0x20000810L), U32(0x20080010L), U32(0x00000000L), U32(0x00000800L), U32(0x00000010L), U32(0x20080800L), U32(0x20080010L), U32(0x20080810L), U32(0x20080000L), U32(0x20000000L), U32(0x00000810L), U32(0x00000010L), U32(0x00080800L), U32(0x00080810L), U32(0x20000800L), U32(0x00000810L), U32(0x20000000L), U32(0x20000800L), U32(0x00080810L), U32(0x20080800L), U32(0x00080010L), U32(0x00000000L), U32(0x20000800L), U32(0x20000000L), U32(0x00000800L), U32(0x20080010L), U32(0x00080000L), U32(0x00080010L), U32(0x20080810L), U32(0x00080800L), U32(0x00000010L), U32(0x20080810L), U32(0x00080800L), U32(0x00080000L), U32(0x20000810L), U32(0x20000010L), U32(0x20080000L), U32(0x00080810L), U32(0x00000000L), U32(0x00000800L), U32(0x20000010L), U32(0x20000810L), U32(0x20080800L), U32(0x20080000L), U32(0x00000810L), U32(0x00000010L), U32(0x20080010L), ], #nibble 6 [ U32(0x00001000L), U32(0x00000080L), U32(0x00400080L), U32(0x00400001L), U32(0x00401081L), U32(0x00001001L), U32(0x00001080L), U32(0x00000000L), U32(0x00400000L), U32(0x00400081L), U32(0x00000081L), U32(0x00401000L), U32(0x00000001L), U32(0x00401080L), U32(0x00401000L), U32(0x00000081L), U32(0x00400081L), U32(0x00001000L), U32(0x00001001L), U32(0x00401081L), U32(0x00000000L), U32(0x00400080L), U32(0x00400001L), U32(0x00001080L), U32(0x00401001L), U32(0x00001081L), U32(0x00401080L), U32(0x00000001L), U32(0x00001081L), U32(0x00401001L), U32(0x00000080L), U32(0x00400000L), U32(0x00001081L), U32(0x00401000L), U32(0x00401001L), U32(0x00000081L), U32(0x00001000L), U32(0x00000080L), U32(0x00400000L), U32(0x00401001L), U32(0x00400081L), U32(0x00001081L), U32(0x00001080L), U32(0x00000000L), U32(0x00000080L), U32(0x00400001L), U32(0x00000001L), U32(0x00400080L), U32(0x00000000L), U32(0x00400081L), U32(0x00400080L), U32(0x00001080L), U32(0x00000081L), U32(0x00001000L), U32(0x00401081L), U32(0x00400000L), U32(0x00401080L), U32(0x00000001L), U32(0x00001001L), U32(0x00401081L), U32(0x00400001L), U32(0x00401080L), U32(0x00401000L), U32(0x00001001L), ], #nibble 7 [ U32(0x08200020L), U32(0x08208000L), U32(0x00008020L), U32(0x00000000L), U32(0x08008000L), U32(0x00200020L), U32(0x08200000L), U32(0x08208020L), U32(0x00000020L), U32(0x08000000L), U32(0x00208000L), U32(0x00008020L), U32(0x00208020L), U32(0x08008020L), U32(0x08000020L), U32(0x08200000L), U32(0x00008000L), U32(0x00208020L), U32(0x00200020L), U32(0x08008000L), U32(0x08208020L), U32(0x08000020L), U32(0x00000000L), U32(0x00208000L), U32(0x08000000L), U32(0x00200000L), U32(0x08008020L), U32(0x08200020L), U32(0x00200000L), U32(0x00008000L), U32(0x08208000L), U32(0x00000020L), U32(0x00200000L), U32(0x00008000L), U32(0x08000020L), U32(0x08208020L), U32(0x00008020L), U32(0x08000000L), U32(0x00000000L), U32(0x00208000L), U32(0x08200020L), U32(0x08008020L), U32(0x08008000L), U32(0x00200020L), U32(0x08208000L), U32(0x00000020L), U32(0x00200020L), U32(0x08008000L), U32(0x08208020L), U32(0x00200000L), U32(0x08200000L), U32(0x08000020L), U32(0x00208000L), U32(0x00008020L), U32(0x08008020L), U32(0x08200000L), U32(0x00000020L), U32(0x08208000L), U32(0x00208020L), U32(0x00000000L), U32(0x08000000L), U32(0x08200020L), U32(0x00008000L), U32(0x00208020L), ], ] #static unsigned long des_skb[8][64]={ des_skb = \ [ #for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 [ U32(0x00000000L),U32(0x00000010L),U32(0x20000000L),U32(0x20000010L), U32(0x00010000L),U32(0x00010010L),U32(0x20010000L),U32(0x20010010L), U32(0x00000800L),U32(0x00000810L),U32(0x20000800L),U32(0x20000810L), U32(0x00010800L),U32(0x00010810L),U32(0x20010800L),U32(0x20010810L), U32(0x00000020L),U32(0x00000030L),U32(0x20000020L),U32(0x20000030L), U32(0x00010020L),U32(0x00010030L),U32(0x20010020L),U32(0x20010030L), U32(0x00000820L),U32(0x00000830L),U32(0x20000820L),U32(0x20000830L), U32(0x00010820L),U32(0x00010830L),U32(0x20010820L),U32(0x20010830L), U32(0x00080000L),U32(0x00080010L),U32(0x20080000L),U32(0x20080010L), U32(0x00090000L),U32(0x00090010L),U32(0x20090000L),U32(0x20090010L), U32(0x00080800L),U32(0x00080810L),U32(0x20080800L),U32(0x20080810L), U32(0x00090800L),U32(0x00090810L),U32(0x20090800L),U32(0x20090810L), U32(0x00080020L),U32(0x00080030L),U32(0x20080020L),U32(0x20080030L), U32(0x00090020L),U32(0x00090030L),U32(0x20090020L),U32(0x20090030L), U32(0x00080820L),U32(0x00080830L),U32(0x20080820L),U32(0x20080830L), U32(0x00090820L),U32(0x00090830L),U32(0x20090820L),U32(0x20090830L), ], #for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 [ U32(0x00000000L),U32(0x02000000L),U32(0x00002000L),U32(0x02002000L), U32(0x00200000L),U32(0x02200000L),U32(0x00202000L),U32(0x02202000L), U32(0x00000004L),U32(0x02000004L),U32(0x00002004L),U32(0x02002004L), U32(0x00200004L),U32(0x02200004L),U32(0x00202004L),U32(0x02202004L), U32(0x00000400L),U32(0x02000400L),U32(0x00002400L),U32(0x02002400L), U32(0x00200400L),U32(0x02200400L),U32(0x00202400L),U32(0x02202400L), U32(0x00000404L),U32(0x02000404L),U32(0x00002404L),U32(0x02002404L), U32(0x00200404L),U32(0x02200404L),U32(0x00202404L),U32(0x02202404L), U32(0x10000000L),U32(0x12000000L),U32(0x10002000L),U32(0x12002000L), U32(0x10200000L),U32(0x12200000L),U32(0x10202000L),U32(0x12202000L), U32(0x10000004L),U32(0x12000004L),U32(0x10002004L),U32(0x12002004L), U32(0x10200004L),U32(0x12200004L),U32(0x10202004L),U32(0x12202004L), U32(0x10000400L),U32(0x12000400L),U32(0x10002400L),U32(0x12002400L), U32(0x10200400L),U32(0x12200400L),U32(0x10202400L),U32(0x12202400L), U32(0x10000404L),U32(0x12000404L),U32(0x10002404L),U32(0x12002404L), U32(0x10200404L),U32(0x12200404L),U32(0x10202404L),U32(0x12202404L), ], #for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 [ U32(0x00000000L),U32(0x00000001L),U32(0x00040000L),U32(0x00040001L), U32(0x01000000L),U32(0x01000001L),U32(0x01040000L),U32(0x01040001L), U32(0x00000002L),U32(0x00000003L),U32(0x00040002L),U32(0x00040003L), U32(0x01000002L),U32(0x01000003L),U32(0x01040002L),U32(0x01040003L), U32(0x00000200L),U32(0x00000201L),U32(0x00040200L),U32(0x00040201L), U32(0x01000200L),U32(0x01000201L),U32(0x01040200L),U32(0x01040201L), U32(0x00000202L),U32(0x00000203L),U32(0x00040202L),U32(0x00040203L), U32(0x01000202L),U32(0x01000203L),U32(0x01040202L),U32(0x01040203L), U32(0x08000000L),U32(0x08000001L),U32(0x08040000L),U32(0x08040001L), U32(0x09000000L),U32(0x09000001L),U32(0x09040000L),U32(0x09040001L), U32(0x08000002L),U32(0x08000003L),U32(0x08040002L),U32(0x08040003L), U32(0x09000002L),U32(0x09000003L),U32(0x09040002L),U32(0x09040003L), U32(0x08000200L),U32(0x08000201L),U32(0x08040200L),U32(0x08040201L), U32(0x09000200L),U32(0x09000201L),U32(0x09040200L),U32(0x09040201L), U32(0x08000202L),U32(0x08000203L),U32(0x08040202L),U32(0x08040203L), U32(0x09000202L),U32(0x09000203L),U32(0x09040202L),U32(0x09040203L), ], #for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 [ U32(0x00000000L),U32(0x00100000L),U32(0x00000100L),U32(0x00100100L), U32(0x00000008L),U32(0x00100008L),U32(0x00000108L),U32(0x00100108L), U32(0x00001000L),U32(0x00101000L),U32(0x00001100L),U32(0x00101100L), U32(0x00001008L),U32(0x00101008L),U32(0x00001108L),U32(0x00101108L), U32(0x04000000L),U32(0x04100000L),U32(0x04000100L),U32(0x04100100L), U32(0x04000008L),U32(0x04100008L),U32(0x04000108L),U32(0x04100108L), U32(0x04001000L),U32(0x04101000L),U32(0x04001100L),U32(0x04101100L), U32(0x04001008L),U32(0x04101008L),U32(0x04001108L),U32(0x04101108L), U32(0x00020000L),U32(0x00120000L),U32(0x00020100L),U32(0x00120100L), U32(0x00020008L),U32(0x00120008L),U32(0x00020108L),U32(0x00120108L), U32(0x00021000L),U32(0x00121000L),U32(0x00021100L),U32(0x00121100L), U32(0x00021008L),U32(0x00121008L),U32(0x00021108L),U32(0x00121108L), U32(0x04020000L),U32(0x04120000L),U32(0x04020100L),U32(0x04120100L), U32(0x04020008L),U32(0x04120008L),U32(0x04020108L),U32(0x04120108L), U32(0x04021000L),U32(0x04121000L),U32(0x04021100L),U32(0x04121100L), U32(0x04021008L),U32(0x04121008L),U32(0x04021108L),U32(0x04121108L), ], #for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 [ U32(0x00000000L),U32(0x10000000L),U32(0x00010000L),U32(0x10010000L), U32(0x00000004L),U32(0x10000004L),U32(0x00010004L),U32(0x10010004L), U32(0x20000000L),U32(0x30000000L),U32(0x20010000L),U32(0x30010000L), U32(0x20000004L),U32(0x30000004L),U32(0x20010004L),U32(0x30010004L), U32(0x00100000L),U32(0x10100000L),U32(0x00110000L),U32(0x10110000L), U32(0x00100004L),U32(0x10100004L),U32(0x00110004L),U32(0x10110004L), U32(0x20100000L),U32(0x30100000L),U32(0x20110000L),U32(0x30110000L), U32(0x20100004L),U32(0x30100004L),U32(0x20110004L),U32(0x30110004L), U32(0x00001000L),U32(0x10001000L),U32(0x00011000L),U32(0x10011000L), U32(0x00001004L),U32(0x10001004L),U32(0x00011004L),U32(0x10011004L), U32(0x20001000L),U32(0x30001000L),U32(0x20011000L),U32(0x30011000L), U32(0x20001004L),U32(0x30001004L),U32(0x20011004L),U32(0x30011004L), U32(0x00101000L),U32(0x10101000L),U32(0x00111000L),U32(0x10111000L), U32(0x00101004L),U32(0x10101004L),U32(0x00111004L),U32(0x10111004L), U32(0x20101000L),U32(0x30101000L),U32(0x20111000L),U32(0x30111000L), U32(0x20101004L),U32(0x30101004L),U32(0x20111004L),U32(0x30111004L), ], #for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 [ U32(0x00000000L),U32(0x08000000L),U32(0x00000008L),U32(0x08000008L), U32(0x00000400L),U32(0x08000400L),U32(0x00000408L),U32(0x08000408L), U32(0x00020000L),U32(0x08020000L),U32(0x00020008L),U32(0x08020008L), U32(0x00020400L),U32(0x08020400L),U32(0x00020408L),U32(0x08020408L), U32(0x00000001L),U32(0x08000001L),U32(0x00000009L),U32(0x08000009L), U32(0x00000401L),U32(0x08000401L),U32(0x00000409L),U32(0x08000409L), U32(0x00020001L),U32(0x08020001L),U32(0x00020009L),U32(0x08020009L), U32(0x00020401L),U32(0x08020401L),U32(0x00020409L),U32(0x08020409L), U32(0x02000000L),U32(0x0A000000L),U32(0x02000008L),U32(0x0A000008L), U32(0x02000400L),U32(0x0A000400L),U32(0x02000408L),U32(0x0A000408L), U32(0x02020000L),U32(0x0A020000L),U32(0x02020008L),U32(0x0A020008L), U32(0x02020400L),U32(0x0A020400L),U32(0x02020408L),U32(0x0A020408L), U32(0x02000001L),U32(0x0A000001L),U32(0x02000009L),U32(0x0A000009L), U32(0x02000401L),U32(0x0A000401L),U32(0x02000409L),U32(0x0A000409L), U32(0x02020001L),U32(0x0A020001L),U32(0x02020009L),U32(0x0A020009L), U32(0x02020401L),U32(0x0A020401L),U32(0x02020409L),U32(0x0A020409L), ], #for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 [ U32(0x00000000L),U32(0x00000100L),U32(0x00080000L),U32(0x00080100L), U32(0x01000000L),U32(0x01000100L),U32(0x01080000L),U32(0x01080100L), U32(0x00000010L),U32(0x00000110L),U32(0x00080010L),U32(0x00080110L), U32(0x01000010L),U32(0x01000110L),U32(0x01080010L),U32(0x01080110L), U32(0x00200000L),U32(0x00200100L),U32(0x00280000L),U32(0x00280100L), U32(0x01200000L),U32(0x01200100L),U32(0x01280000L),U32(0x01280100L), U32(0x00200010L),U32(0x00200110L),U32(0x00280010L),U32(0x00280110L), U32(0x01200010L),U32(0x01200110L),U32(0x01280010L),U32(0x01280110L), U32(0x00000200L),U32(0x00000300L),U32(0x00080200L),U32(0x00080300L), U32(0x01000200L),U32(0x01000300L),U32(0x01080200L),U32(0x01080300L), U32(0x00000210L),U32(0x00000310L),U32(0x00080210L),U32(0x00080310L), U32(0x01000210L),U32(0x01000310L),U32(0x01080210L),U32(0x01080310L), U32(0x00200200L),U32(0x00200300L),U32(0x00280200L),U32(0x00280300L), U32(0x01200200L),U32(0x01200300L),U32(0x01280200L),U32(0x01280300L), U32(0x00200210L),U32(0x00200310L),U32(0x00280210L),U32(0x00280310L), U32(0x01200210L),U32(0x01200310L),U32(0x01280210L),U32(0x01280310L), ], #for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 [ U32(0x00000000L),U32(0x04000000L),U32(0x00040000L),U32(0x04040000L), U32(0x00000002L),U32(0x04000002L),U32(0x00040002L),U32(0x04040002L), U32(0x00002000L),U32(0x04002000L),U32(0x00042000L),U32(0x04042000L), U32(0x00002002L),U32(0x04002002L),U32(0x00042002L),U32(0x04042002L), U32(0x00000020L),U32(0x04000020L),U32(0x00040020L),U32(0x04040020L), U32(0x00000022L),U32(0x04000022L),U32(0x00040022L),U32(0x04040022L), U32(0x00002020L),U32(0x04002020L),U32(0x00042020L),U32(0x04042020L), U32(0x00002022L),U32(0x04002022L),U32(0x00042022L),U32(0x04042022L), U32(0x00000800L),U32(0x04000800L),U32(0x00040800L),U32(0x04040800L), U32(0x00000802L),U32(0x04000802L),U32(0x00040802L),U32(0x04040802L), U32(0x00002800L),U32(0x04002800L),U32(0x00042800L),U32(0x04042800L), U32(0x00002802L),U32(0x04002802L),U32(0x00042802L),U32(0x04042802L), U32(0x00000820L),U32(0x04000820L),U32(0x00040820L),U32(0x04040820L), U32(0x00000822L),U32(0x04000822L),U32(0x00040822L),U32(0x04040822L), U32(0x00002820L),U32(0x04002820L),U32(0x00042820L),U32(0x04042820L), U32(0x00002822L),U32(0x04002822L),U32(0x00042822L),U32(0x04042822L), ] ]ntlmaps-0.9.9.0.1/lib/config.py0000664000076400007640000000450510252206443015441 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import string import getopt #------------------------------------------------------------------------------------------- def read_config(fname): "" res = {} section_name = 'DEFAULT' buf = open(fname).readlines() for i in range(len(buf)): t = buf[i] t = string.split(t, '#')[0] t = string.strip(t) if t: if t[0] == '[' and t[-1] == ']': section_name = string.strip(t[1:-1]) if section_name: res[section_name] = {} else: parts = string.split(t, ':') if len(parts) > 1: res[section_name][string.strip(parts[0])] = string.strip(parts[1]) return res #------------------------------------------------------------------------------------------- # Thanks Janek Schwarz for this addition. def findConfigFileNameInArgv(argv, configFileDir=''): """ Resolves configuration file. Resolution goes as follows: if the command switch '-c' is given it's argument is taken as the config file. Otherwise the function falls back to 'server.cfg' in the current directory. """ configFileName = configFileDir+'server.cfg' optionsList, notUsedArguments = getopt.getopt(argv[1:], 'c:') for i in optionsList: option, value = i if option == '-c' and value != '': configFileName = value break return configFileName ntlmaps-0.9.9.0.1/lib/server.py0000664000076400007640000001433410252207255015505 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import socket, thread, sys, signal, getpass import proxy_client, www_client, monitor_upstream, ntlm_procs #-------------------------------------------------------------- class AuthProxyServer: #-------------------------------------------------------------- def __init__(self, config): self.config = config self.MyHost = '' self.ListenPort = self.config['GENERAL']['LISTEN_PORT'] self.sigLock = thread.allocate_lock() # For locking in the sigHandler self.monLock = thread.allocate_lock() # For keeping the monitor thread sane self.watchUpstream = 0 if not self.config['NTLM_AUTH']['NTLM_TO_BASIC']: if not self.config['NTLM_AUTH']['PASSWORD']: tries = 3 print '------------------------' while tries and (not self.config['NTLM_AUTH']['PASSWORD']): tries = tries - 1 self.config['NTLM_AUTH']['PASSWORD'] = getpass.getpass('Your NT password to be used:') if not self.config['NTLM_AUTH']['PASSWORD']: print 'Sorry. PASSWORD is required, bye.' sys.exit(1) else: # TODO: migrate this properly so placeholders aren't required self.config['NTLM_AUTH']['USER'] = 'placeholder_username' self.config['NTLM_AUTH']['PASSWORD'] = 'placeholder_password' # hashed passwords calculation self.config['NTLM_AUTH']['LM_HASHED_PW'] = ntlm_procs.create_LM_hashed_password(self.config['NTLM_AUTH']['PASSWORD']) self.config['NTLM_AUTH']['NT_HASHED_PW'] = ntlm_procs.create_NT_hashed_password(self.config['NTLM_AUTH']['PASSWORD']) #-------------------------------------------------------------- def run(self): signal.signal(signal.SIGINT, self.sigHandler) if self.config['GENERAL']['PARENT_PROXY'] and self.config['GENERAL']['AVAILABLE_PROXY_LIST']: self.watchUpstream = 1 self.monitor = monitor_upstream.monitorThread(self.config, signal.SIGINT) thread.start_new_thread(self.monitor.run, ()) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((self.MyHost, self.ListenPort)) except socket.error: print "ERROR: Could not create socket. Possibly port %s is still being used by another process." % self.config['GENERAL']['LISTEN_PORT'] sys.exit(1) print 'Now listening at %s on port %s' % (self.config['GENERAL']['HOST'], self.config['GENERAL']['LISTEN_PORT']) while(1): s.listen(self.config['GENERAL']['MAX_CONNECTION_BACKLOG']) try: conn, addr = s.accept() if self.config['GENERAL']['ALLOW_EXTERNAL_CLIENTS']: self.client_run(conn, addr) else: if addr[0] in self.config['GENERAL']['FRIENDLY_IPS']: self.client_run(conn, addr) else: conn.close() except socket.error: pass s.close() #-------------------------------------------------------------- def client_run(self, conn, addr): if self.config['GENERAL']['PARENT_PROXY']: # working with MS Proxy if self.watchUpstream: # Locking here is really more of a 'nice to have'; # if performance suffers on heavy load we can trade # drops here for drops on bad proxy later. self.monLock.acquire() c = proxy_client.proxy_HTTP_Client(conn, addr, self.config) self.monitor.threadsToKill.append(c) self.monLock.release() else: c = proxy_client.proxy_HTTP_Client(conn, addr, self.config) else: # working with MS IIS and any other c = www_client.www_HTTP_Client(conn, addr, self.config) thread.start_new_thread(c.run, ()) #-------------------------------------------------------------- def sigHandler(self, signum=None, frame=None): if signum == signal.SIGINT: if self.watchUpstream: if self.sigLock.acquire(0): old_monitor = self.monitor self.config['GENERAL']['AVAILABLE_PROXY_LIST'].insert(0, self.config['GENERAL']['PARENT_PROXY']) self.monLock.acquire() # Keep locked section as small as possible self.config['GENERAL']['PARENT_PROXY'] = self.config['GENERAL']['AVAILABLE_PROXY_LIST'].pop() self.monitor = monitor_upstream.monitorThread(self.config, signal.SIGINT) self.monLock.release() print "Moving to proxy server: "+self.config['GENERAL']['PARENT_PROXY'] old_monitor.alive = 0 thread.start_new_thread(self.monitor.run, ()) map(lambda x: x.exit(), old_monitor.threadsToKill) old_monitor.die() # Protected from recursion by lock self.sigLock.release() else: # SIGINT is only special if we are in upstream mode: print 'Got SIGINT, exiting now...' sys.exit(1) else: print 'Got SIGNAL '+str(signum)+', exiting now...' sys.exit(1) return ntlmaps-0.9.9.0.1/lib/logger.py0000664000076400007640000000322110252205210015434 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import time #----------------------------------------------------------------------- class Logger: "provides facility for loggin messages during runtime" #----------------------------------------------------------------------- def __init__(self, log_name, debug_level = 1): "logger init routine" self.log_name = log_name self.debug_level = debug_level #----------------------------------------------------------------------- def log(self, str): "writes string to log file" if self.debug_level: tstr = '' # tstr = '(' + time.strftime('%H:%M:%S', time.localtime(time.time())) + ') ' # time.clock() fptr = open(self.log_name, 'a') fptr.write(tstr + str) fptr.close() ntlmaps-0.9.9.0.1/lib/des.py0000664000076400007640000000613010252205210014732 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import des_c, utils #--------------------------------------------------------------------- class DES: des_c_obj = None #----------------------------------------------------------------- def __init__(self, key_str): "" k = str_to_key56(key_str) k = key56_to_key64(k) key_str = utils.lst2str(k) self.des_c_obj = des_c.DES(key_str) #----------------------------------------------------------------- def encrypt(self, plain_text): "" return self.des_c_obj.encrypt(plain_text) #----------------------------------------------------------------- def decrypt(self, crypted_text): "" return self.des_c_obj.decrypt(crypted_text) #--------------------------------------------------------------------- #Some Helpers #--------------------------------------------------------------------- DESException = 'DESException' #--------------------------------------------------------------------- def str_to_key56(key_str): "" if type(key_str) != type(''): #rise DESException, 'ERROR. Wrong key type.' pass if len(key_str) < 7: key_str = key_str + '\000\000\000\000\000\000\000'[:(7 - len(key_str))] key_56 = [] for i in key_str[:7]: key_56.append(ord(i)) return key_56 #--------------------------------------------------------------------- def key56_to_key64(key_56): "" key = [] for i in range(8): key.append(0) key[0] = key_56[0]; key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); key[7] = (key_56[6] << 1) & 0xFF; key = set_key_odd_parity(key) return key #--------------------------------------------------------------------- def set_key_odd_parity(key): "" for i in range(len(key)): for k in range(7): bit = 0 t = key[i] >> k bit = (t ^ bit) & 0x1 key[i] = (key[i] & 0xFE) | bit return key ntlmaps-0.9.9.0.1/lib/des_c.py0000664000076400007640000002245510252206443015255 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # from U32 import U32 # --NON ASCII COMMENT ELIDED-- #typedef unsigned char des_cblock[8]; #define HDRSIZE 4 def c2l(c): "char[4] to unsigned long" l = U32(c[0]) l = l | (U32(c[1]) << 8) l = l | (U32(c[2]) << 16) l = l | (U32(c[3]) << 24) return l def c2ln(c,l1,l2,n): "char[n] to two unsigned long???" c = c + n l1, l2 = U32(0), U32(0) f = 0 if n == 8: l2 = l2 | (U32(c[7]) << 24) f = 1 if f or (n == 7): l2 = l2 | (U32(c[6]) << 16) f = 1 if f or (n == 6): l2 = l2 | (U32(c[5]) << 8) f = 1 if f or (n == 5): l2 = l2 | U32(c[4]) f = 1 if f or (n == 4): l1 = l1 | (U32(c[3]) << 24) f = 1 if f or (n == 3): l1 = l1 | (U32(c[2]) << 16) f = 1 if f or (n == 2): l1 = l1 | (U32(c[1]) << 8) f = 1 if f or (n == 1): l1 = l1 | U32(c[0]) return (l1, l2) def l2c(l): "unsigned long to char[4]" c = [] c.append(int(l & U32(0xFF))) c.append(int((l >> 8) & U32(0xFF))) c.append(int((l >> 16) & U32(0xFF))) c.append(int((l >> 24) & U32(0xFF))) return c def n2l(c, l): "network to host long" l = U32(c[0] << 24) l = l | (U32(c[1]) << 16) l = l | (U32(c[2]) << 8) l = l | (U32(c[3])) return l def l2n(l, c): "host to network long" c = [] c.append(int((l >> 24) & U32(0xFF))) c.append(int((l >> 16) & U32(0xFF))) c.append(int((l >> 8) & U32(0xFF))) c.append(int((l ) & U32(0xFF))) return c def l2cn(l1, l2, c, n): "" for i in range(n): c.append(0x00) f = 0 if f or (n == 8): c[7] = int((l2 >> 24) & U32(0xFF)) f = 1 if f or (n == 7): c[6] = int((l2 >> 16) & U32(0xFF)) f = 1 if f or (n == 6): c[5] = int((l2 >> 8) & U32(0xFF)) f = 1 if f or (n == 5): c[4] = int((l2 ) & U32(0xFF)) f = 1 if f or (n == 4): c[3] = int((l1 >> 24) & U32(0xFF)) f = 1 if f or (n == 3): c[2] = int((l1 >> 16) & U32(0xFF)) f = 1 if f or (n == 2): c[1] = int((l1 >> 8) & U32(0xFF)) f = 1 if f or (n == 1): c[0] = int((l1 ) & U32(0xFF)) f = 1 return c[:n] # array of data # static unsigned long des_SPtrans[8][64]={ # static unsigned long des_skb[8][64]={ from des_data import des_SPtrans, des_skb def D_ENCRYPT(tup, u, t, s): L, R, S = tup #print 'LRS1', L, R, S, u, t, '-->', u = (R ^ s[S]) t = R ^ s[S + 1] t = ((t >> 4) + (t << 28)) L = L ^ (des_SPtrans[1][int((t ) & U32(0x3f))] | \ des_SPtrans[3][int((t >> 8) & U32(0x3f))] | \ des_SPtrans[5][int((t >> 16) & U32(0x3f))] | \ des_SPtrans[7][int((t >> 24) & U32(0x3f))] | \ des_SPtrans[0][int((u ) & U32(0x3f))] | \ des_SPtrans[2][int((u >> 8) & U32(0x3f))] | \ des_SPtrans[4][int((u >> 16) & U32(0x3f))] | \ des_SPtrans[6][int((u >> 24) & U32(0x3f))]) #print 'LRS:', L, R, S, u, t return ((L, R, S), u, t, s) def PERM_OP (tup, n, m): "tup - (a, b, t)" a, b, t = tup t = ((a >> n) ^ b) & m b = b ^ t a = a ^ (t << n) return (a, b, t) def HPERM_OP (tup, n, m): "tup - (a, t)" a, t = tup t = ((a << (16 - n)) ^ a) & m a = a ^ t ^ (t >> (16 - n)) return (a, t) shifts2 = [0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0] class DES: KeySched = None # des_key_schedule def __init__(self, key_str): # key - UChar[8] key = [] for i in key_str: key.append(ord(i)) #print 'key:', key self.KeySched = des_set_key(key) #print 'schedule:', self.KeySched, len(self.KeySched) def decrypt(self, str): # block - UChar[] block = [] for i in str: block.append(ord(i)) #print block block = des_ecb_encrypt(block, self.KeySched, 0) res = '' for i in block: res = res + (chr(i)) return res def encrypt(self, str): # block - UChar[] block = [] for i in str: block.append(ord(i)) block = des_ecb_encrypt(block, self.KeySched, 1) res = '' for i in block: res = res + (chr(i)) return res #------------------------ def des_encript(input, ks, encrypt): # input - U32[] # output - U32[] # ks - des_key_shedule - U32[2][16] # encrypt - int # l, r, t, u - U32 # i - int # s - U32[] l = input[0] r = input[1] t = U32(0) u = U32(0) r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0fL)) l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffffL)) r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333L)) l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ffL)) r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555L)) t = (r << 1)|(r >> 31) r = (l << 1)|(l >> 31) l = t s = ks # ??????????????? #print l, r if(encrypt): for i in range(0, 32, 4): rtup, u, t, s = D_ENCRYPT((l, r, i + 0), u, t, s) l = rtup[0] r = rtup[1] rtup, u, t, s = D_ENCRYPT((r, l, i + 2), u, t, s) r = rtup[0] l = rtup[1] else: for i in range(30, 0, -4): rtup, u, t, s = D_ENCRYPT((l, r, i - 0), u, t, s) l = rtup[0] r = rtup[1] rtup, u, t, s = D_ENCRYPT((r, l, i - 2), u, t, s) r = rtup[0] l = rtup[1] #print l, r l = (l >> 1)|(l << 31) r = (r >> 1)|(r << 31) r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555L)) l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ffL)) r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333L)) l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffffL)) r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0fL)) output = [l] output.append(r) l, r, t, u = U32(0), U32(0), U32(0), U32(0) return output def des_ecb_encrypt(input, ks, encrypt): # input - des_cblock - UChar[8] # output - des_cblock - UChar[8] # ks - des_key_shedule - U32[2][16] # encrypt - int #print input l0 = c2l(input[0:4]) l1 = c2l(input[4:8]) ll = [l0] ll.append(l1) #print ll ll = des_encript(ll, ks, encrypt) #print ll l0 = ll[0] l1 = ll[1] output = l2c(l0) output = output + l2c(l1) #print output l0, l1, ll[0], ll[1] = U32(0), U32(0), U32(0), U32(0) return output def des_set_key(key): # key - des_cblock - UChar[8] # schedule - des_key_schedule # register unsigned long c,d,t,s; # register unsigned char *in; # register unsigned long *k; # register int i; #k = schedule # in = key k = [] c = c2l(key[0:4]) d = c2l(key[4:8]) t = U32(0) d, c, t = PERM_OP((d, c, t), 4, U32(0x0f0f0f0fL)) c, t = HPERM_OP((c, t), -2, U32(0xcccc0000L)) d, t = HPERM_OP((d, t), -2, U32(0xcccc0000L)) d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555L)) c, d, t = PERM_OP((c, d, t), 8, U32(0x00ff00ffL)) d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555L)) d = (((d & U32(0x000000ffL)) << 16)|(d & U32(0x0000ff00L))|((d & U32(0x00ff0000L)) >> 16)|((c & U32(0xf0000000L)) >> 4)) c = c & U32(0x0fffffffL) for i in range(16): if (shifts2[i]): c = ((c >> 2)|(c << 26)) d = ((d >> 2)|(d << 26)) else: c = ((c >> 1)|(c << 27)) d = ((d >> 1)|(d << 27)) c = c & U32(0x0fffffffL) d = d & U32(0x0fffffffL) s= des_skb[0][int((c ) & U32(0x3f))]|\ des_skb[1][int(((c>> 6) & U32(0x03))|((c>> 7) & U32(0x3c)))]|\ des_skb[2][int(((c>>13) & U32(0x0f))|((c>>14) & U32(0x30)))]|\ des_skb[3][int(((c>>20) & U32(0x01))|((c>>21) & U32(0x06)) | ((c>>22) & U32(0x38)))] t= des_skb[4][int((d ) & U32(0x3f) )]|\ des_skb[5][int(((d>> 7) & U32(0x03))|((d>> 8) & U32(0x3c)))]|\ des_skb[6][int((d>>15) & U32(0x3f) )]|\ des_skb[7][int(((d>>21) & U32(0x0f))|((d>>22) & U32(0x30)))] #print s, t k.append(((t << 16)|(s & U32(0x0000ffffL))) & U32(0xffffffffL)) s = ((s >> 16)|(t & U32(0xffff0000L))) s = (s << 4)|(s >> 28) k.append(s & U32(0xffffffffL)) schedule = k return schedule ntlmaps-0.9.9.0.1/lib/proxy_client.py0000664000076400007640000007671010252207321016716 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import string, socket, thread, select, time import logger, http_header, utils, ntlm_auth, basic_auth class proxy_HTTP_Client: #----------------------------------------------------------------------- def __init__(self, client_socket, address, config): "" self.config = config self.ntlm_auther = ntlm_auth.ntlm_auther() # experimental code self.basic_auther = basic_auth.basic_auther() # experimental code end self.proxy_authorization_tried = 0 self.www_authorization_tried = 0 self.tunnel_mode = 0 # define log files self.logger_bin_client = logger.Logger('%s-%d.bin.client' % address, self.config['DEBUG']['BIN_DEBUG']) self.logger_bin_rserver = logger.Logger('%s-%d.bin.rserver' % address, self.config['DEBUG']['BIN_DEBUG']) self.logger_auth = logger.Logger('%s-%d.auth' % address, self.config['DEBUG']['AUTH_DEBUG']) self.logger = logger.Logger('%s-%d' % address, self.config['DEBUG']['DEBUG']) # set it to 1 till we will connect actually??? # No!!! In that case we have unexpected exit if the client sends its header slow. self.rserver_socket_closed = 1 # Yes ! # This is a flag that means that we have not tried connecting to # the remote host. And self.rserver_socket_closed = 1 is not requiring # to finish thread. # Do not like this solution. self.first_run = 1 self.client_socket_closed = 0 self.stop_request = 0 # must finish thread self.current_rserver_net_location = '' self.rserver_socket = None self.client_socket = client_socket self.client_address = address self.rserver_head_obj = None self.rserver_current_data_pos =0 self.rserver_all_got = 0 self.rserver_data_length = 0 self.rserver_header_sent = 0 self.rserver_data_sent = 0 self.rserver_buffer = '' self.client_head_obj = None self.client_current_data_pos =0 self.client_all_got = 0 self.client_data_length = 0 self.client_header_sent = 0 self.client_data_sent = 0 self.client_buffer = '' self.client_sent_data = '' # init record to debug_log self.logger.log('%s Version %s\n' % (time.strftime('%d.%m.%Y %H:%M:%S', time.localtime(time.time())), self.config['GENERAL']['VERSION'])) #----------------------------------------------------------------------- def run(self): "" if self.config['DEBUG']['SCR_DEBUG']: print 'Connected from %s:%d' % self.client_address while(not self.stop_request): # wait for data if not (self.rserver_buffer or self.client_buffer): # if buffers are empty """ if not self.rserver_socket_closed: select.select([self.rserver_socket.fileno(), self.client_socket.fileno()], [], [], 5.0) else: # if there is no connection to remote server select.select([self.client_socket.fileno()], [], [], 5.0) """ # Experimental code. We fight bug when we do not get all the header in the # first try and have stop request because rserver_socket_closed==1 # So let's try change socket_closed to socket. which is None if there is no # connection if not self.rserver_socket_closed: try: select.select([self.rserver_socket.fileno(), self.client_socket.fileno()], [], [], 5.0) except (socket.error, select.error, ValueError): thread.exit() else: # if there is no connection to remote server try: select.select([self.client_socket.fileno()], [], [], 5.0) except socket.error: thread.exit() # client part self.run_client_loop() if self.tunnel_mode: self.tunnel_client_data() if not self.client_header_sent and self.client_head_obj: if not self.rserver_socket_closed: # if connected we have to check whether we are connected to the right host. # if not then close connection self.check_connected_remote_server() #if self.rserver_socket_closed: if self.rserver_socket_closed: # connect remote server if we have not yet self.connect_rserver() self.log_url() self.send_client_header() if self.client_header_sent and (not self.client_data_sent): self.send_client_data() if self.client_data_sent and self.rserver_data_sent: # NOTE: we need to know if the request method is HEAD or CONNECT, so we cannot # proceed to the next request header until response is not worked out self.check_tunnel_mode() self.reset_client() if self.config['DEBUG']['SCR_DEBUG']: print '\b.', # Remote server part if not self.rserver_socket_closed: # if there is a connection to remote server self.run_rserver_loop() if self.tunnel_mode: self.tunnel_rserver_data() if (not self.rserver_header_sent) and self.rserver_head_obj: self.auth_routine() # NTLM authorization if (not self.rserver_header_sent) and self.rserver_head_obj: self.send_rserver_header() self.check_rserver_response() if self.rserver_header_sent and (not self.rserver_data_sent): self.send_rserver_data() if self.client_head_obj == None and self.rserver_data_sent: self.reset_rserver() self.logger.log('*** Request completed.\n') self.check_stop_request() self.exit() if self.config['DEBUG']['SCR_DEBUG']: print 'Finished %s:%d' % self.client_address #----------------------------------------------------------------------- def fix_client_header(self): "" self.logger.log('*** Replacing values in client header...') if self.config.has_key('CLIENT_HEADER'): for i in self.config['CLIENT_HEADER'].keys(): self.client_head_obj.del_param(i) self.client_head_obj.add_param_value(i, self.config['CLIENT_HEADER'][i]) self.logger.log('Done.\n') self.logger.log('*** New client header:\n=====\n' + self.client_head_obj.__repr__()) else: self.logger.log('No need.\n*** There is no "CLIENT_HEADER" section in server.cfg.\n') #----------------------------------------------------------------------- def run_rserver_loop(self): "" try: res = select.select([self.rserver_socket.fileno()], [], [], 0.0) except (socket.error, ValueError): self.logger.log('*** Exception in select() on server socket.\n') thread.exit() if res[0]: try: socket_data = self.rserver_socket.recv(4096) except: socket_data = '' self.logger.log('*** Exception in remote server recv() happend.\n') if not socket_data: self.rserver_socket_closed = 1 self.logger.log('*** Remote server closed connection. (Server buffer - %d bytes)\n' % len(self.rserver_buffer)) else: socket_data = '' if socket_data: self.logger_bin_rserver.log(socket_data) self.rserver_buffer = self.rserver_buffer + socket_data if not self.rserver_head_obj and not self.tunnel_mode: self.rserver_head_obj, rest = http_header.extract_server_header(self.rserver_buffer) # Code for ugly MS response without right delimiter. # Just '\015\012' at the end. Then it closes connection. if self.rserver_socket_closed and self.rserver_buffer and (not self.rserver_head_obj): self.rserver_head_obj, rest = http_header.extract_server_header(self.rserver_buffer + '\015\012') if self.rserver_head_obj: self.logger.log("*** There is an bad MS Proxy response header with only one new line at the end in the buffer.\n") # End of code. if self.rserver_head_obj: self.logger.log("*** Got remote server response header.\n") self.rserver_buffer = rest self.logger.log('*** Remote server header:\n=====\n' + self.rserver_head_obj.__repr__()) self.guess_rserver_data_length() self.check_rserver_data_length() #----------------------------------------------------------------------- def run_client_loop(self): "" try: res = select.select([self.client_socket.fileno()], [], [], 0.0) except (socket.error, select.error, ValueError): thread.exit() if res[0]: try: socket_data = self.client_socket.recv(4096) except: socket_data = '' self.logger.log('*** Exception in client recv() happend.\n') if not socket_data: self.client_socket_closed = 1 self.logger.log('*** Client closed connection.\n') else: socket_data = '' if socket_data: self.logger_bin_client.log(socket_data) self.client_buffer = self.client_buffer + socket_data if not self.client_head_obj and not self.tunnel_mode: self.client_head_obj, rest = http_header.extract_client_header(self.client_buffer) if self.client_head_obj: self.logger.log("*** Got client request header.\n") self.client_buffer = rest self.logger.log('*** Client header:\n=====\n' + self.client_head_obj.__repr__()) self.guess_client_data_length() # mask real values and do transforms self.fix_client_header() self.check_client_data_length() #----------------------------------------------------------------------- def send_rserver_header(self): "" self.logger.log('*** Sending remote server response header to client...') ok = self.rserver_head_obj.send(self.client_socket) if ok: self.rserver_header_sent = 1 self.logger.log('Done.\n') else: self.client_socket_closed = 1 self.logger.log('Failed.\n') #----------------------------------------------------------------------- def send_rserver_data(self): "" if self.rserver_buffer and (not self.rserver_data_sent): if self.rserver_data_length: if self.rserver_all_got: data = self.rserver_buffer[:self.rserver_data_length - self.rserver_current_data_pos] self.rserver_buffer = self.rserver_buffer[self.rserver_data_length - self.rserver_current_data_pos:] else: data = self.rserver_buffer self.rserver_buffer = '' self.rserver_current_data_pos = self.rserver_current_data_pos + len(data) else: data = self.rserver_buffer self.rserver_buffer = '' try: self.client_socket.send(data) self.logger.log('*** Sent %d bytes to client. (all - %d, len - %d)\n' % (len(data), self.rserver_all_got, self.rserver_data_length)) if self.rserver_all_got: self.rserver_data_sent = 1 self.logger.log('*** Sent ALL the data from remote server to client. (Server buffer - %d bytes)\n' % len(self.rserver_buffer)) except: self.logger.log('*** Exception by sending data to client. Client closed connection.\n') self.client_socket_closed = 1 else: self.logger.log("*** No server's data to send to the client. (server's buffer - %d bytes)\n" % len(self.rserver_buffer)) #----------------------------------------------------------------------- def send_client_header(self): "" self.logger.log('*** Sending client request header to remote server...') ok = self.client_head_obj.send(self.rserver_socket) if ok: self.client_header_sent = 1 self.logger.log('Done.\n') else: self.rserver_socket_closed = 1 self.logger.log('Failed.\n') #----------------------------------------------------------------------- def send_client_data(self): "" if self.client_buffer and (not self.client_data_sent): if self.client_data_length: if self.client_all_got: data = self.client_buffer[:self.client_data_length - self.client_current_data_pos] self.client_buffer = self.client_buffer[self.client_data_length - self.client_current_data_pos:] else: data = self.client_buffer self.client_buffer = '' self.client_current_data_pos = self.client_current_data_pos + len(data) else: data = self.client_buffer self.client_buffer = '' try: self.rserver_socket.send(data) self.client_sent_data = self.client_sent_data + data self.logger.log('*** Sent %d bytes to remote server. (all - %d)\n' % (len(data), self.client_all_got)) if self.client_all_got: self.client_data_sent = 1 self.logger.log('*** Sent ALL the data from client to remote server. (Client buffer - %d bytes)\n' % len(self.client_buffer)) except: self.logger.log('*** Exception during sending data to remote server. Remote server closed connection.\n') self.rserver_socket_closed = 1 #----------------------------------------------------------------------- def reset_rserver(self): "" self.logger.log('*** Resetting remote server status...') self.rserver_head_obj = None self.rserver_current_data_pos =0 self.rserver_all_got = 0 self.rserver_data_length = 0 self.rserver_header_sent = 0 self.rserver_data_sent = 0 #self.rserver_buffer = '' self.logger.log('Done. (Server buffer - %d bytes)\n' % len(self.rserver_buffer)) #----------------------------------------------------------------------- def reset_client(self): "" self.logger.log('*** Resetting client status...') self.client_head_obj = None self.client_current_data_pos =0 self.client_all_got = 0 self.client_data_length = 0 self.client_header_sent = 0 self.client_data_sent = 0 #self.client_buffer = '' self.client_sent_data = '' self.logger.log('Done. (Client buffer - %d bytes)\n' % len(self.client_buffer)) #----------------------------------------------------------------------- def rollback_client_data(self): "" # some activity for POST and PUT getting to work with authorization # part of data might be sent before we have got 407 error # so we have to get those data back if self.client_sent_data: self.logger.log("*** Sent %s bytes and have to roll back POST/PUT data transfer. (Client's buffer - %d bytes)\n" % (len(self.client_sent_data), len(self.client_buffer))) self.client_buffer = self.client_sent_data + self.client_buffer self.client_current_data_pos =0 self.client_all_got = 0 self.client_data_sent = 0 self.client_sent_data = '' self.logger.log("Rollback Done. (Client's buffer - %d bytes)\n" % len(self.client_buffer)) #----------------------------------------------------------------------- def tunnel_rserver_data(self): "" if self.rserver_buffer: data = self.rserver_buffer self.rserver_buffer = '' try: self.client_socket.send(data) self.logger.log('*** Tunnelled %d bytes to client.\n' % len(data)) except: self.logger.log('*** Exception by tunnelling data to client. Client closed connection.\n') self.client_socket_closed = 1 #----------------------------------------------------------------------- def tunnel_client_data(self): "" if self.client_buffer: data = self.client_buffer self.client_buffer = '' try: self.rserver_socket.send(data) self.logger.log('*** Tunnelled %d bytes to remote server.\n' % len(data)) except: self.logger.log('*** Exception by tunnelling data to remote server. Remote server closed connection.\n') self.rserver_socket_closed = 1 #----------------------------------------------------------------------- def guess_rserver_data_length(self): "" code = self.rserver_head_obj.get_http_code() try: c_method = self.client_head_obj.get_http_method() except AttributeError: # Problem with remote end of connection self.logger.log('*** Exception getting http code from client_head_obj -- remote end closed connection??\n') thread.exit() if code == '304' or code == '204' or code[0] == '1': self.rserver_all_got = 1 self.rserver_data_sent = 1 self.rserver_data_length = 0 self.logger.log('*** Remote server response is %s and it must not have any body.\n' % code) # we had problem here if the responce was some kind of error. Then there may be # some body. # This time let's try to check for 4** responses to fix the problem. if (c_method == 'HEAD' or c_method == 'CONNECT') and (code[0] != '4'): self.rserver_all_got = 1 self.rserver_data_sent = 1 self.rserver_data_length = 0 self.logger.log("*** Remote server response to the '%s' request. It must not have any body.\n" % c_method) if not self.rserver_all_got: try: self.rserver_data_length = int(self.rserver_head_obj.get_param_values('Content-Length')[0]) self.logger.log("*** Server 'Content-Length' found to be %d.\n" % self.rserver_data_length) if self.rserver_data_length == 0: self.rserver_all_got = 1 self.rserver_data_sent = 1 except: self.rserver_data_length = 0 self.logger.log("*** Could not find server 'Content-Length' parameter.\n") #----------------------------------------------------------------------- def guess_client_data_length(self): "" if not self.client_head_obj.has_param('Content-Length') and not self.client_head_obj.has_param('Transfer-Encode'): self.client_all_got = 1 self.client_data_sent = 1 self.client_data_length = 0 self.logger.log("*** Client request header does not have 'Content-Length' or 'Transfer-Encoding' parameter and it must not have any body.\n") if not self.client_all_got: try: self.client_data_length = int(self.client_head_obj.get_param_values('Content-Length')[0]) self.logger.log("*** Client 'Content-Length' found to be %s.\n" % self.client_data_length) if self.client_data_length == 0: self.client_all_got = 1 self.client_data_sent = 1 except: self.client_data_length = 0 self.logger.log("*** Could not find client 'Content-Length' parameter.\n") #----------------------------------------------------------------------- def check_rserver_data_length(self): "" if self.rserver_data_length: if self.rserver_data_length <= (len(self.rserver_buffer) + self.rserver_current_data_pos): self.rserver_all_got = 1 #----------------------------------------------------------------------- def check_client_data_length(self): "" if self.client_data_length: if self.client_data_length <= (len(self.client_buffer) + self.client_current_data_pos): self.client_all_got = 1 #----------------------------------------------------------------------- def check_tunnel_mode(self): "" p_code = self.rserver_head_obj.get_http_code() c_request = self.client_head_obj.get_http_method() if c_request == 'CONNECT' and p_code == '200': self.logger.log("*** Successful 'CONNECT' request detected. Going to tunnel mode.\n") self.tunnel_mode = 1 #----------------------------------------------------------------------- def log_url(self): "" if self.config['GENERAL']['URL_LOG']: t = time.strftime('%d.%m.%Y %H:%M:%S', time.localtime(time.time())) m = self.client_head_obj.get_http_method() url = self.client_head_obj.get_http_url() v = self.client_head_obj.get_http_version() p1 = '%s %s %s %s ' % (t, m, url, v) p2 = '(from %s:%s)' % self.client_address self.config['GENERAL']['URL_LOG_LOCK'].acquire() self.config['GENERAL']['URL_LOGGER'].log(p1 + p2 + '\n') self.config['GENERAL']['URL_LOG_LOCK'].release() #----------------------------------------------------------------------- def check_stop_request(self): "" reason = '' if self.rserver_socket_closed and not self.first_run: self.stop_request = 1 reason = "remote server closed connection" if self.rserver_head_obj: # if we POSTing or PUTting some info and Proxy closed connection # with error 407, we would like to try to authorize ourselves before giving up. if (not self.proxy_authorization_tried) and self.rserver_head_obj.get_http_code() == '407': self.stop_request = 0 if (not self.www_authorization_tried) and self.rserver_head_obj.get_http_code() == '401': self.stop_request = 0 if self.client_socket_closed: self.stop_request = 1 reason = "client closed connection" if self.rserver_socket_closed and self.client_socket_closed: # actually redundant case, but anyway... self.stop_request = 1 reason = "remote server and client closed connections" if self.stop_request: self.logger.log("*** Termination conditions detected (%s). Stop Request issued.\n" % reason) #----------------------------------------------------------------------- def exit(self): "" self.logger.log('*** Finishing procedure started.\n') if self.rserver_socket_closed and self.rserver_buffer and (not self.client_socket_closed): self.logger.log('*** There are some data to be sent to client in the remote server buffer.\n') self.tunnel_rserver_data() if self.client_socket_closed and self.client_buffer and (not self.rserver_socket_closed): self.logger.log('*** There are some data to be sent to remote server in the client buffer.\n') self.tunnel_client_data() self.logger.log('*** Closing thread...') if self.client_socket: self.client_socket.close() if self.rserver_socket: self.rserver_socket.close() self.logger.log('Done.\n') # thread.exit() #------------------------------------------------- def connect_rserver(self): "" self.logger.log('*** Connecting to remote server...') self.first_run = 0 rs = self.config['GENERAL']['PARENT_PROXY'] rsp = self.config['GENERAL']['PARENT_PROXY_PORT'] self.logger.log('(%s:%d)...' % (rs, rsp)) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((rs, rsp)) self.rserver_socket = s self.rserver_socket_closed = 0 self.current_rserver_net_location = '%s:%d' % (rs, rsp) self.logger.log('Done.\n') except: self.rserver_socket_closed = 1 self.logger.log('Failed.\n') self.exit() thread.exit() #------------------------------------------------- def close_rserver(self): "" self.logger.log('*** Closing connection to the remote server...') self.rserver_socket.close() self.rserver_socket_closed = 1 self.current_rserver_net_location = '' self.logger.log('Done.\n') #------------------------------------------------- def close_client(self): "" self.logger.log('*** Closing connection to the client...') self.client_socket.close() self.client_socket_closed = 1 self.logger.log('Done.\n') #----------------------------------------------------------------------- def auth_407(self): "" auth = self.rserver_head_obj.get_param_values('Proxy-Authenticate') upper_auth = [] msg = '' for i in auth: msg = msg + ", %s" % i upper_auth.append(string.upper(i)) self.logger.log('*** Authentication methods allowed: ' + msg[2:] + '\n') # NOTE that failed auth is detected now just after any failed atempt. # May it will be of use to keep trying till all the methods will fail. if self.proxy_authorization_tried: self.logger.log('*** Looks like our authorization failed.\n*** Passing 407 to client.\n') else: self.proxy_authorization_tried = 1 self.rollback_client_data() # Method selector. Should try the most secure method. if 'NTLM' in upper_auth: self.logger.log('*** Using NTLM authentication method.\n') #self.proxy_ntlm_authorization() self.ntlm_auther.proxy_ntlm_authentication(self) elif 'BASIC_' in upper_auth: self.logger.log('*** Using Basic authentication method.\n') #self.basic_authorization() self.basic_auther.proxy_basic_authentication(self) else: self.logger.log('*** There are no supported authentication methods in remote server response.\n') self.logger.log('*** Passing 407 to client.\n') #----------------------------------------------------------------------- def auth_401(self): "" auth = self.rserver_head_obj.get_param_values('Www-Authenticate') upper_auth = [] msg = '' for i in auth: msg = msg + ", %s" % i upper_auth.append(string.upper(i)) self.logger.log('*** Authentication methods allowed: ' + msg[2:] + '\n') # NOTE that failed auth is detected now just after any failed atempt. # May it will be of use to keep trying till all the methods will fail. if self.www_authorization_tried: self.logger.log('*** Looks like our authorization failed.\n*** Passing 401 to client.\n') else: self.www_authorization_tried = 1 self.rollback_client_data() # Method selector. Should try the most secure method. if 'NTLM' in upper_auth: self.logger.log('*** Using NTLM authentication method.\n') #self.www_ntlm_authorization() self.ntlm_auther.www_ntlm_authentication(self) elif 'BASIC_' in upper_auth: self.logger.log('*** Using Basic authentication method.\n') #self.basic_authorization() self.basic_auther.www_basic_authentication(self) else: self.logger.log('*** There are no supported authentication methods in the Web Server response.\n') self.logger.log('*** Passing 401 to client.\n') #----------------------------------------------------------------------- def auth_routine(self): "" self.logger.log('*** Authentication routine started.\n') code = self.rserver_head_obj.get_http_code() if code == '407': self.logger.log('*** Got Error 407 - "Proxy authentication required".\n') self.auth_407() elif code == '401': self.logger.log('*** Got Error 401 - "WWW authentication required".\n') self.auth_401() else: self.logger.log('*** Authentication not required.\n') self.logger.log('*** Authentication routine finished.\n') #----------------------------------------------------------------------- def check_rserver_response(self): "" if self.rserver_header_sent == 1: code = self.rserver_head_obj.get_http_code() if code == '100': # reaction on response '100' - 'Continue' self.logger.log("*** Got and sent to client response '100'. Need to prepare for final response.\n") self.reset_rserver() if code not in ['401', '407']: # if we have at least one request successfully served # then we have to clear up authentication flags to be ready # to authenticate yourself again if there is such a need if self.proxy_authorization_tried or self.www_authorization_tried: self.proxy_authorization_tried = 0 self.www_authorization_tried = 0 self.logger.log('*** Lowered authentication flags down. As the code is neither 401 nor 407.\n') #----------------------------------------------------------------------- # Need not for proxy module... def check_connected_remote_server(self): "" pass ntlmaps-0.9.9.0.1/lib/ntlm_procs.py0000664000076400007640000000525010252205210016341 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import string import des, md4, utils #--------------------------------------------------------------------- #takes a 21 byte array and treats it as 3 56-bit DES keys. The #8 byte plaintext is encrypted with each key and the resulting 24 #bytes are stored in the result array def calc_resp(keys_str, plain_text): "keys_str - hashed password" "plain_text - nonce from server" res = '' dobj = des.DES(keys_str[0:7]) res = res + dobj.encrypt(plain_text[0:8]) dobj = des.DES(keys_str[7:14]) res = res + dobj.encrypt(plain_text[0:8]) dobj = des.DES(keys_str[14:21]) res = res + dobj.encrypt(plain_text[0:8]) return res #--------------------------------------------------------------------- def create_LM_hashed_password(passwd): "setup LanManager password" "create LanManager hashed password" lm_pw = '\000' * 14 passwd = string.upper(passwd) if len(passwd) < 14: lm_pw = passwd + lm_pw[len(passwd) - 14:] else: lm_pw = passwd[0:14] # do hash magic_lst = [0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25] magic_str = utils.lst2str(magic_lst) res = '' dobj = des.DES(lm_pw[0:7]) res = res + dobj.encrypt(magic_str) dobj = des.DES(lm_pw[7:14]) res = res + dobj.encrypt(magic_str) # addig zeros to get 21 bytes string res = res + '\000\000\000\000\000' return res #--------------------------------------------------------------------- def create_NT_hashed_password(passwd): "create NT hashed password" # we have to have UNICODE password pw = utils.str2unicode(passwd) # do MD4 hash md4_context = md4.new() md4_context.update(pw) res = md4_context.digest() # addig zeros to get 21 bytes string res = res + '\000\000\000\000\000' return res ntlmaps-0.9.9.0.1/lib/www_client.py0000664000076400007640000001157710252205210016354 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import string, socket, thread, select, time import logger, http_header, utils import ntlm_auth, basic_auth import proxy_client class www_HTTP_Client(proxy_client.proxy_HTTP_Client): #------------------------------------------------- def connect_rserver(self): "" self.logger.log('*** Connecting to remote server...') self.first_run = 0 # we don't have proxy then we have to connect server by ourselves rs, rsp = self.client_head_obj.get_http_server() self.logger.log('(%s:%d)...' % (rs, rsp)) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((rs, rsp)) self.rserver_socket = s self.rserver_socket_closed = 0 self.current_rserver_net_location = '%s:%d' % (rs, rsp) self.logger.log('Done.\n') except: self.rserver_socket_closed = 1 self.logger.log('Failed.\n') self.exit() thread.exit() #----------------------------------------------------------------------- def fix_client_header(self): "" self.logger.log('*** Replacing values in client header...') if self.config.has_key('CLIENT_HEADER'): for i in self.config['CLIENT_HEADER'].keys(): self.client_head_obj.del_param(i) self.client_head_obj.add_param_value(i, self.config['CLIENT_HEADER'][i]) self.logger.log('Done.\n') # self.logger.log('*** New client header:\n=====\n' + self.client_head_obj.__repr__()) else: self.logger.log('No need.\n*** There is no "CLIENT_HEADER" section in server.cfg.\n') self.logger.log("*** Working as selfcontained proxy, then have to change client header.\n") self.logger.log("*** Remake url format in client header...") self.client_head_obj.make_right_header() self.logger.log('Done.\n') self.client_head_obj.del_param('Keep-Alive') self.logger.log("*** Just killed 'Keep-Alive' value in the header.\n") # Code which converts 'Proxy-Connection' value to 'Connection' # I am not sure that it is needed at all # May be it is just useless activity self.logger.log("*** Looking for 'Proxy-Connection' in client header...") pconnection = self.client_head_obj.get_param_values('Proxy-Connection') if pconnection: # if we have 'Proxy-Connection' self.logger.log("there are some.\n") wconnection = self.client_head_obj.get_param_values('Connection') if wconnection: # if we have 'Connection' as well self.logger.log("*** There is a 'Connection' value in the header.\n") self.client_head_obj.del_param('Proxy-Connection') self.logger.log("*** Just killed 'Proxy-Connection' value in the header.\n") else: self.logger.log("*** There is no 'Connection' value in the header.\n") self.client_head_obj.del_param('Proxy-Connection') for i in pconnection: self.client_head_obj.add_param_value('Connection', i) self.logger.log("*** Changed 'Proxy-Connection' to 'Connection' header value.\n") else: self.logger.log("there aren't any.\n") # End of doubtable code. # Show reworked header. self.logger.log('*** New client header:\n=====\n' + self.client_head_obj.__repr__()) #----------------------------------------------------------------------- def check_connected_remote_server(self): "" # if we are working as a standalone proxy server rs, rsp = self.client_head_obj.get_http_server() if self.current_rserver_net_location != '%s:%d' % (rs, rsp): # if current connection is not we need then close it. self.logger.log('*** We had wrong connection for new request so we have to close it.\n') self.close_rserver() ntlmaps-0.9.9.0.1/lib/md4.py0000664000076400007640000002143510252206443014661 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # # MD4 validation data md4_test= [ ('', 0x31d6cfe0d16ae931b73c59d7e0c089c0L), ("a", 0xbde52cb31de33e46245e05fbdbd6fb24L), ("abc", 0xa448017aaf21d8525fc10ae87aa6729dL), ("message digest", 0xd9130a8164549fe818874806e1c7014bL), ("abcdefghijklmnopqrstuvwxyz", 0xd79e1c308aa5bbcdeea8ed63df412da9L), ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0x043f8582f241db351ce627e153e7f0e4L), ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", 0xe33b4ddc9c38f2199c3e7b164fcc0536L), ] #------------------------------------------------------------------------------ from U32 import U32 class MD4: A = None B = None C = None D = None count, len1, len2 = None, None, None buf = [] #----------------------------------------------------- def __init__(self): self.A = U32(0x67452301L) self.B = U32(0xefcdab89L) self.C = U32(0x98badcfeL) self.D = U32(0x10325476L) self.count, self.len1, self.len2 = U32(0L), U32(0L), U32(0L) self.buf = [0x00] * 64 #----------------------------------------------------- def __repr__(self): r = 'A = %s, \nB = %s, \nC = %s, \nD = %s.\n' % (self.A.__repr__(), self.B.__repr__(), self.C.__repr__(), self.D.__repr__()) r = r + 'count = %s, \nlen1 = %s, \nlen2 = %s.\n' % (self.count.__repr__(), self.len1.__repr__(), self.len2.__repr__()) for i in range(4): for j in range(16): r = r + '%4s ' % hex(self.buf[i+j]) r = r + '\n' return r #----------------------------------------------------- def make_copy(self): dest = new() dest.len1 = self.len1 dest.len2 = self.len2 dest.A = self.A dest.B = self.B dest.C = self.C dest.D = self.D dest.count = self.count for i in range(self.count): dest.buf[i] = self.buf[i] return dest #----------------------------------------------------- def update(self, str): buf = [] for i in str: buf.append(ord(i)) ilen = U32(len(buf)) #print (ilen) # --NON ASCII COMMENT ELIDED-- if (long(self.len1 + (ilen << 3)) < long(self.len1)): self.len2 = self.len2 + U32(1) self.len1 = self.len1 + (ilen << 3) self.len2 = self.len2 + (ilen >> 29) # print int(self.len1), int(self.len2) #print (self.len1), (self.len2) L = U32(0) bufpos = 0 while (long(ilen) > 0): if (64 - long(self.count)) < long(ilen): L = U32(64 - long(self.count)) else: L = ilen for i in range(int(L)): self.buf[i + int(self.count)] = buf[i + bufpos] self.count = self.count + L ilen = ilen - L bufpos = bufpos + int(L) #print self.count, L, ilen if (long(self.count) == 64L): self.count = U32(0L) X = [] i = 0 for j in range(16): X.append(U32(self.buf[i]) + (U32(self.buf[i+1]) << 8) + \ (U32(self.buf[i+2]) << 16) + (U32(self.buf[i+3]) << 24)) i = i + 4 A = self.A B = self.B C = self.C D = self.D A = f1(A,B,C,D, 0, 3, X) D = f1(D,A,B,C, 1, 7, X) C = f1(C,D,A,B, 2,11, X) B = f1(B,C,D,A, 3,19, X) A = f1(A,B,C,D, 4, 3, X) D = f1(D,A,B,C, 5, 7, X) C = f1(C,D,A,B, 6,11, X) B = f1(B,C,D,A, 7,19, X) A = f1(A,B,C,D, 8, 3, X) D = f1(D,A,B,C, 9, 7, X) C = f1(C,D,A,B,10,11, X) B = f1(B,C,D,A,11,19, X) A = f1(A,B,C,D,12, 3, X) D = f1(D,A,B,C,13, 7, X) C = f1(C,D,A,B,14,11, X) B = f1(B,C,D,A,15,19, X) A = f2(A,B,C,D, 0, 3, X) D = f2(D,A,B,C, 4, 5, X) C = f2(C,D,A,B, 8, 9, X) B = f2(B,C,D,A,12,13, X) A = f2(A,B,C,D, 1, 3, X) D = f2(D,A,B,C, 5, 5, X) C = f2(C,D,A,B, 9, 9, X) B = f2(B,C,D,A,13,13, X) A = f2(A,B,C,D, 2, 3, X) D = f2(D,A,B,C, 6, 5, X) C = f2(C,D,A,B,10, 9, X) B = f2(B,C,D,A,14,13, X) A = f2(A,B,C,D, 3, 3, X) D = f2(D,A,B,C, 7, 5, X) C = f2(C,D,A,B,11, 9, X) B = f2(B,C,D,A,15,13, X) A = f3(A,B,C,D, 0, 3, X) D = f3(D,A,B,C, 8, 9, X) C = f3(C,D,A,B, 4,11, X) B = f3(B,C,D,A,12,15, X) A = f3(A,B,C,D, 2, 3, X) D = f3(D,A,B,C,10, 9, X) C = f3(C,D,A,B, 6,11, X) B = f3(B,C,D,A,14,15, X) A = f3(A,B,C,D, 1, 3, X) D = f3(D,A,B,C, 9, 9, X) C = f3(C,D,A,B, 5,11, X) B = f3(B,C,D,A,13,15, X) A = f3(A,B,C,D, 3, 3, X) D = f3(D,A,B,C,11, 9, X) C = f3(C,D,A,B, 7,11, X) B = f3(B,C,D,A,15,15, X) self.A = self.A + A self.B = self.B + B self.C = self.C + C self.D = self.D + D #print self #----------------------------------------------------- def digest(self): res = [0x00] * 16 s = [0x00] * 8 padding = [0x00] * 64 padding[0] = 0x80 padlen, oldlen1, oldlen2 = U32(0), U32(0), U32(0) temp = self.make_copy() oldlen1 = temp.len1 oldlen2 = temp.len2 if (56 <= long(self.count)): padlen = U32(56 - long(self.count) + 64) else: padlen = U32(56 - long(self.count)) #print int(padlen) temp.update(int_array2str(padding[:int(padlen)])) #print temp s[0]= (oldlen1) & U32(0xFF) s[1]=((oldlen1) >> 8) & U32(0xFF) s[2]=((oldlen1) >> 16) & U32(0xFF) s[3]=((oldlen1) >> 24) & U32(0xFF) s[4]= (oldlen2) & U32(0xFF) s[5]=((oldlen2) >> 8) & U32(0xFF) s[6]=((oldlen2) >> 16) & U32(0xFF) s[7]=((oldlen2) >> 24) & U32(0xFF) temp.update(int_array2str(s)) #print temp res[ 0]= temp.A & U32(0xFF) res[ 1]=(temp.A >> 8) & U32(0xFF) res[ 2]=(temp.A >> 16) & U32(0xFF) res[ 3]=(temp.A >> 24) & U32(0xFF) res[ 4]= temp.B & U32(0xFF) res[ 5]=(temp.B >> 8) & U32(0xFF) res[ 6]=(temp.B >> 16) & U32(0xFF) res[ 7]=(temp.B >> 24) & U32(0xFF) res[ 8]= temp.C & U32(0xFF) res[ 9]=(temp.C >> 8) & U32(0xFF) res[10]=(temp.C >> 16) & U32(0xFF) res[11]=(temp.C >> 24) & U32(0xFF) res[12]= temp.D & U32(0xFF) res[13]=(temp.D >> 8) & U32(0xFF) res[14]=(temp.D >> 16) & U32(0xFF) res[15]=(temp.D >> 24) & U32(0xFF) return int_array2str(res) #------------------------------------------------------------------------------ def F(x, y, z): return (((x) & (y)) | ((~x) & (z))) def G(x, y, z): return (((x) & (y)) | ((x) & (z)) | ((y) & (z))) def H(x, y, z): return ((x) ^ (y) ^ (z)) def ROL(x, n): return (((x) << n) | ((x) >> (32-n))) def f1(a, b, c, d, k, s, X): return ROL(a + F(b, c, d) + X[k], s) def f2(a, b, c, d, k, s, X): return ROL(a + G(b, c, d) + X[k] + U32(0x5a827999L), s) def f3(a, b, c, d, k, s, X): return ROL(a + H(b, c, d) + X[k] + U32(0x6ed9eba1L), s) def int_array2str(array): str = '' for i in array: str = str + chr(i) return str new = MD4 ntlmaps-0.9.9.0.1/lib/http_header.py0000664000076400007640000002657410252207171016474 0ustar dixonddixond# This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import string, urlparse http_debug_file_name = 'http.debug' #----------------------------------------------------------------------- # tests client's header for correctness def test_client_http_header(header_str): "" request = string.split(header_str, '\012')[0] parts = string.split(request) # we have to have at least 3 words in the request # poor check if len(parts) < 3: return 0 else: return 1 #----------------------------------------------------------------------- # tests server's response header for correctness def test_server_http_header(header_str): "" response = string.split(header_str, '\012')[0] parts = string.split(response) # we have to have at least 2 words in the response # poor check if len(parts) < 2: return 0 else: return 1 #----------------------------------------------------------------------- def extract_http_header_str(buffer): "" # let's remove possible leading newlines t = string.lstrip(buffer) # searching for the RFC header's end delimiter = '\015\012\015\012' header_end = string.find(t, delimiter) if header_end < 0: # may be it is defective header made by junkbuster delimiter = '\012\012' header_end = string.find(t, delimiter) if header_end >=0: # we have found it, possibly ld = len(delimiter) header_str = t[0:header_end + ld] # Let's check if it is a proper header if test_server_http_header(header_str) or test_client_http_header(header_str): # if yes then let's do our work if (header_end + ld) >= len(t): rest_str = '' else: rest_str = t[header_end + ld:] else: # if not then let's leave the buffer as it is # NOTE: if there is some junk before right header we will never # find that header. Till timeout, I think. Not that good solution. header_str = '' rest_str = buffer else: # there is no complete header in the buffer header_str = '' rest_str = buffer return (header_str, rest_str) #----------------------------------------------------------------------- def extract_server_header(buffer): "" header_str, rest_str = extract_http_header_str(buffer) if header_str: header_obj = HTTP_SERVER_HEAD(header_str) else: header_obj = None return (header_obj, rest_str) #----------------------------------------------------------------------- def extract_client_header(buffer): "" header_str, rest_str = extract_http_header_str(buffer) if header_str: header_obj = HTTP_CLIENT_HEAD(header_str) else: header_obj = None return (header_obj, rest_str) #----------------------------------------------------------------------- def capitalize_value_name(str): "" tl = string.split(str, '-') for i in range(len(tl)): tl[i] = string.capitalize(tl[i]) return string.join(tl, '-') #----------------------------------------------------------------------- # some helper classes #----------------------------------------------------------------------- class HTTP_HEAD: "" pass #------------------------------- def __init__(self, head_str): "" self.head_source = '' self.params = None self.fields = None self.order_list = [] self.head_source = head_str head_str = string.strip(head_str) records = string.split(head_str, '\012') # Dealing with response line #fields = string.split(records[0], ' ', 2) t = string.split(string.strip(records[0])) fields = t[:2] + [string.join(t[2:])] self.fields = [] for i in fields: self.fields.append(string.strip(i)) # Dealing with params params = {} order_list = [] for i in records[1:]: parts = string.split(string.strip(i), ':', 1) pname = string.lower(string.strip(parts[0])) if not params.has_key(pname): params[pname] = [] order_list.append(string.lower(pname)) try: params[pname].append(string.strip(parts[1])) except: msg = "ERROR: Exception in head parsing. ValueName: '%s'" % pname #print msg self.debug(msg) self.params = params self.order_list = order_list #------------------------------- def debug(self, message): "" try: f = open(http_debug_file_name, 'a') f.write(message) f.write('\n=====\n') f.write(self.head_source) f.close() except IOError: pass # Yes, yes, I know, this is just sweeping it under the rug... # TODO: implement a persistent filehandle for logging debug messages to. #------------------------------- def copy(self): "" import copy return copy.deepcopy(self) #------------------------------- def get_param_values(self, param_name): "" param_name = string.lower(param_name) if self.params.has_key(param_name): return self.params[param_name] else: return [] #------------------------------- def del_param(self, param_name): "" param_name = string.lower(param_name) if self.params.has_key(param_name): del self.params[param_name] #------------------------------- def has_param(self, param_name): "" param_name = string.lower(param_name) return self.params.has_key(param_name) #------------------------------- def add_param_value(self, param_name, value): "" param_name = string.lower(param_name) if not self.params.has_key(param_name): self.params[param_name] = [] if param_name not in self.order_list: self.order_list.append(param_name) self.params[param_name].append(value) #------------------------------- def replace_param_value(self, param_name, value): "" self.del_param(param_name) self.add_param_value(param_name, value) #------------------------------- def __repr__(self, delimiter='\n'): "" res = '' cookies = '' res = string.join(self.fields, ' ') + '\n' for i in self.order_list: if self.params.has_key(i): if i == 'cookie': for k in self.params[i]: cookies = cookies + capitalize_value_name(i) + ': ' + k + '\n' else: for k in self.params[i]: res = res + capitalize_value_name(i) + ': ' + k + '\n' res = res + cookies res = res + '\n' return res #------------------------------- def send(self, socket): "" #""" res = '' cookies = '' res = string.join(self.fields, ' ') + '\015\012' for i in self.order_list: if self.params.has_key(i): if i == 'cookie': for k in self.params[i]: cookies = cookies + capitalize_value_name(i) + ': ' + k + '\015\012' else: for k in self.params[i]: res = res + capitalize_value_name(i) + ': ' + k + '\015\012' res = res + cookies res = res + '\015\012' #""" #res = self.__repr__('\015\012') # NOTE!!! 0.9.1 worked, 0.9.5 and 0.9.7 did not with MSN Messenger. # We had problem here that prevent MSN Messenger from working. # Some work is needed to make __rerp__ working instead of current code.. try: #socket.send(self.head_source) socket.send(res) # self.debug(res) return 1 except: return 0 #----------------------------------------------------------------------- class HTTP_SERVER_HEAD(HTTP_HEAD): #------------------------------- def get_http_version(self): "" return self.fields[0] #------------------------------- def get_http_code(self): "" return self.fields[1] #------------------------------- def get_http_message(self): "" return self.fields[2] #----------------------------------------------------------------------- class HTTP_CLIENT_HEAD(HTTP_HEAD): #------------------------------- def get_http_version(self): "" return self.fields[2] #------------------------------- def get_http_method(self): "" return self.fields[0] #------------------------------- def get_http_url(self): "" return self.fields[1] #------------------------------- def set_http_url(self, new_url): "" self.fields[1] = new_url #------------------------------- # There is some problem with www request header... # not all servers want to answer to requests with full url in request # but want have net location in 'Host' value and path in url. def make_right_header(self): "" url_tuple = urlparse.urlparse(self.get_http_url()) net_location = url_tuple[1] self.replace_param_value('Host', net_location) path = urlparse.urlunparse(tuple(['', ''] + list(url_tuple[2:]))) self.set_http_url(path) #------------------------------- def get_http_server(self): "" # trying to get host from url url_tuple = urlparse.urlparse(self.get_http_url()) net_location = url_tuple[1] # if there was no host in url then get it from 'Host' value if not net_location: net_location = self.get_param_values('Host')[0] if not net_location: net_location = 'localhost' # trying to parse user:passwd@www.some.domain:8080 # is it needed? if '@' in net_location: cred, net_location = string.split(net_location, '@') if ':' in net_location: server, port = string.split(net_location, ':') port = int(port) else: server = net_location port = 80 return server, port ntlmaps-0.9.9.0.1/runserver.bat0000664000076400007640000000006710252205210015565 0ustar dixonddixond@echo off "c:\program files\python\python.exe" main.pyntlmaps-0.9.9.0.1/Install.txt0000664000076400007640000000237310252206717015230 0ustar dixonddixondINSTALLATION ------------ 1. Edit server.cfg to match your preferences 2. Run the main.py script. Windows: --------- runserver.bat Unix: --------- /path/to/main.py & or python /path/to/main.py 3. Try it! Go to your browser and set 127.0.0.1 at port 5865 as your HTTP/FTP proxy server. 5865 is the default port, if you have not changed it in server.cfg, change this appropriatelly. --------------------------------------------------------------------------------------- Tip: --------------------------------------------------------------------------------------- There is a command line option "-c config_file_name" for pointing APS to config files other than the default server.cfg in the working directory. --------------------------------------------------------------------------------------- If server fails to authenticate you at MS Proxy with NTLM, you may want to try changing FULL_NTLM in server.cfg to 1. Such message in debug file suggest this problem: *** Authentication methods allowed: NTLM *** Looks like our authorization failed. *** Passing 407(401) to client. --------------------------------------------------------------------------------------- ntlmaps-0.9.9.0.1/server.cfg0000664000076400007640000001624610252207171015047 0ustar dixonddixond#======================================================================== [GENERAL] LISTEN_PORT:5865 # If you want APS to authenticate you at WWW servers using NTLM then just leave this # value blank like PARENT_PROXY: and APS will connect to web servers directly. # You can specify more than one proxy by leaving a space between each one, and # APS will detect when one fails and automatically fail-over to the next. EG: #PARENT_PROXY:first_proxy second_proxy third_proxy # And NOTE that NTLM cannot pass through another proxy server. PARENT_PROXY:your_parentproxy PARENT_PROXY_PORT:8080 # APS will poll the upstream proxy and attempt to fail-over to a new one if it doesn't # get a response within an appropriate time frame. The amount of time that it will # wait for a response before attempting fail-over is specified, in seconds, below: PARENT_PROXY_TIMEOUT:15 # Set to 1 if you want to grant this authorization service to clients from other computers. # NOTE: all the users from other hosts that will be using you copy of APS for authentication # will be using your credentials in NTLM auth at the remote host. ALLOW_EXTERNAL_CLIENTS:0 # If you want to allow some other but not all computers to use your proxy for authorization, # just set ALLOW_EXTERNAL_CLIENTS:0 and put friendly IP addresses here. # Use space as a delimiter. # NOTE that special addesses don't work here (192.168.3.0 for example). FRIENDLY_IPS: # Requested URLs are written to "url.log" file. May be useful. URL_LOG:0 # When a network service listens for connections, there is a maximum number of connection # attempts to that service that the underlying OS will allow to backlog waiting for a response # before the OS will start dropping new connection attempts with 'Connection refused'. The # standard method of determining the maximum number of backlogged connections is to use the # SOMAXCONN constant, which is supposed to represent the maximum number that an OS will support # (for example, 5 on Windows 2000 Pro, and 200 on Windows 2000 server). However, because this # is a statically compiled value in a Python distribution, usually this instead represents the # the most conservative value (5 on all Windows platforms, and 128 on the GNU/Linux variant I # tried). So if you are running (for example) a massively threaded/parallel download manager, # the default value of, say, 5, or whatever SOMAXCONN happens to be set to, may be too low and # cause some connections to fail. The value below can be set to any integer (it seems that # Python just silently caps values above the hard limit for the underlying platform), or it can # be set to the special value of SOMAXCONN (i.e. MAX_CONNECTION_BACKLOG:SOMAXCONN), to use # whatever this value happens to be set to in your Python build. Setting this higher than # necessary may cause APS to consume more memory than you needed to. MAX_CONNECTION_BACKLOG:5 #======================================================================== [CLIENT_HEADER] # This section describes what and how the server should change in the clients headers. # Made in order to prevent parent proxy from seeing that you are using wget instead of IE5.5 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, application/vnd.ms-powerpoint, */* User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98) # for windows 2000 emulation ;) # User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT5) # You can uncomment these chages in client's header to mimic IE5+ better, but in this case # you may expirience problems with *.html if your client does not really handle compression. #Accept-Encoding: gzip, deflate #======================================================================== [NTLM_AUTH] # Optional value, if leaved blank then APS will use gethostname() to determine # host's name. # NOTE1: If you Linux host name differs from Windows host name then it may be that # MS server wont recognize you host at all and wont grant you access # to resources requested. Then you have to use this option and APS will use # this name in NTLM negotiations. # NOTE2: There are several reports that you can successfully use "foreign" host name # here. Say, if user may access a resource from 'host1' and may not from 'host2' # then there is a chance that APS running on 'host2' with NT_HOSTNAME:host1 will # be able to be granted access to the restricted resource. However use this on # you own risk as such a trick may be considered as a hack or something. NT_HOSTNAME: # Windows Domain. # NOTE: it is not full qualified internet domain, but windows network domain. NT_DOMAIN:your_domain # What user's name to use during authorization. It may differ form real current username. # If you enable NTLM_TO_BASIC, below, you can either leave this blank or simply # hash it out. USER:username_to_use # Password. Just leave it blank here and server will request it at the start time, # or, if you enable NTLM_TO_BASIC, below, you can either leave this blank or simply # hash it out, and you *won't* be prompted for a password at start time. PASSWORD:your_nt_password # These two options replace old FULL_NTLM option. # NTLM authentication consists virtually of two parts: LM and NT. Windows95/98 use # only LM part, WindowsNT/2000 can use NT and LM or just NT part. # Almost always using just LM part will be enough. I had several reports # about LM and NT requirement and no about just NT. # So try to setup 1, 1 only if you have enough reasons to do so and when you understand # what you are doing. # 0, 0 is an illegal combination # NOTE: if you change these options then you have to setup flag option accordingly. LM_PART:1 NT_PART:0 # Highly experimental option. See research.txt for details. # LM - 06820000 # NT - 05820000 # LM + NT - 07820000 NTLM_FLAGS: 06820000 # This option makes APS try to translate NTLM authentication to very usual "Basic" # scheme. Almost all http clients know it. With this option set to 1 user will be requested # by his browser to enter his credentials and these username and password will be used by # APS for NTLM authentication at MS Proxy server or Web server. # In such a case different users can use one runnig APS with their own credentials. # NOTE1: currently translation works so it allows only one try for entering # username/password. If you make a mistake you will have to restart you browser. # NOTE2: With debug:1 basic username/password will be written in log file in clear # text format. I could try hide it, but the basic scheme is so weak that anybody # who had access to APS would be able to get it. NTLM_TO_BASIC:0 #======================================================================== [DEBUG] # Set this to 1 if you want to see debug info in many log files. One per connection. DEBUG:0 # Set this to 1 to get even more debug info. BIN_DEBUG:0 # Set this to 1 to see some strange activity on screen. Actually you won't want it. SCR_DEBUG:0 # Not actually a debug option but gives you some details on authentication process # into *.auth logs. Also see research.txt. AUTH_DEBUG:0 ntlmaps-0.9.9.0.1/changelog.txt0000664000076400007640000002151310366274127015554 0ustar dixonddixondVersion 0.9.9.0.1 Friday 27th January 2006 Darryl Dixon - Minor bugfix for Python 1.5.2 compatibility Version 0.9.9 Thursday 10th February 2005 Darryl Dixon - Re-release 0.9.8.11 as 0.9.9 stable Version 0.9.8.11 Friday 14th January 2005 Darryl Dixon - Fixed use of split() function that broke Python 1.5.2 compatibility in config_affairs.py - Added traps for ValueError in proxy_client.py for Python 1.5.2 compatibility - Split testing of upstream proxy codepath in monitor_upstream.py into Python 2.x and 1.5.2 branches Version 0.9.8.10 Friday 24th December Darryl Dixon - Caught another exception condition for failed upstream proxy in monitor_upstream.py - Reworked program flow in monitor_upstream.py and server.py to fix 100% cpu bug introduced in 0.9.8.9 Version 0.9.8.9 Thursday 2nd December Darryl Dixon - Change default values for PARENT_PROXY_PORT to 8080 and PARENT_PROXY_TIMEOUT to 15 in server.cfg - Squashed a bug in config_affairs.py that broke LISTEN_PORT when no PARENT_PROXY specified - Caught another exception condition in proxy_client.py - Clarified 'Could not create socket' error in server.py to reference port number - Added clause in monitor_upstream.py to prevent automatic failover in the case of single upstream proxy - Removed redundant 'pass' statement from server.py - Caught another exception condition for failed upstream proxy in monitor_upstream.py - BUGFIX sourceforge ID#1070604: Added exception handler for call to socket.gethostbyname_ex() in config_affairs.py - Moved copyright banner to appear earlier in initialisation - Added TODO to monitor_upstream.py - BUGFIX sourceforge ID#1070605: Added exception handler for call to open() in http_header.py Version 0.9.8.8 Tuesday 16th November 2004 Darryl Dixon - Squashed bug introduced in 0.9.8.7 with bad logger entry in proxy_client.py - Cleaned up handling of 'PASSWORD' config item in config_affairs.py to squash potential bug - Modified server.cfg to enhance description of 'PASSWORD' and 'USER' items - Slightly updated the readme.txt Version 0.9.8.7 Friday 12th November 2004 Darryl Dixon - BUGFIX sourceforge ID#798824: Modified exception trap for c_method = self.client_head_obj.get_http_method() in proxy_client.py - Replaced several TODOs from proxy_client.py with logger entries - Removed bogus 'accepting connections' comment from main.py - Squashed bug with SOMAXCONN setup trapping wrong exception - Moved some initialisation code from main.py to server.py - Clarified some code dealing with configuration items in main.py - Modified some of the startup text/order - Sanified processing of int() items in config_affairs.py - Removed redundant statement from NTLM section of config_affairs.py - Modified ntlm_auth.py to reflect NTLM_TO_BASIC as already an int - Properly corrected second instance of sourceforge ID#1061067 in ntlm_auth.py Version 0.9.8.6 Thursday 11th November 2004 Darryl Dixon - Added MAX_CONNECTION_BACKLOG configuration item - FEATURE/BUGFIX sourceforge request ID#831606: MAX_CONNECTION_BACKLOG now allows configurable listener - Clarified some historical entries in changelog - Minor cleanups of error text in config_affairs.py Version 0.9.8.5 Wednesday 10th November 2004 Darryl Dixon - Added PARENT_PROXY_TIMEOUT configuration item - Squashed a typo bug in config_affairs.py for HOST_IP_LIST - Updated Install.txt - BUGFIX sourceforge request ID#707725 and ID#1061067: Fixed 2 instances of error in ntlm_auth.py Version 0.9.8.4 Tuesday 9th November 2004 Darryl Dixon - Changed method of delivering signal to just call sigHandler() directly - Seriously reworked programme flow of monitor_upstream.py to accomodate change above - Added locking of critical sections to keep threads sane - Caught another exception in proxy_client.py Version 0.9.8.3 Sunday 7th November 2004 Darryl Dixon - Changed SIGALRM timer to new custom timerThread() (more portable) - Cleaned up signal handling to accomodate new timer implementation - Caught some more exceptions in proxy_client.py - Fixed corner case which b0rked timer Version 0.9.8.2 Friday 5th November 2004 Darryl Dixon - Added extra exception trapping for fault conditions in proxy_client.py - Added __init__.py to begin making ntlmaps relocatable - Changed from SIGUSR1 to SIGINT to make more portable - Removed two non-ascii comments causing warnings from compiler in md4.py and des_c.py Version 0.9.8.1 Monday 1st November 2004 Darryl Dixon - Added monitor_upstream.py for upstream mode that detects upstream proxy failure - Added ability to specify multiple upstream proxies for failover Verison 0.9.8 - Fri May 17 04:07:20 MSD 2002 - internal redesign - config file redesign - fixed bug during HTTPS CONNECT authentication. - fixed bug with UNICODE string conversion in NTLM msg3 creation code. - no need in proxy port when proxy is not used - fixed minor bug with an exception that was eraised if there was no http header in server's response. - MSN Messenger and clients alike work again. It had been broken since APS 0.9.1 - minor bug in header remake (Proxy Connection -> Connection) - fixed bug when client sends its header slowly and clients thread exits before doing anything useful. This was broken since version 0.9.7 - new optional value in config file NT_HOSTNAME (see comment in server.cfg). - DOMAIN value in config is now NT_DOMAIN, to make it clear what domain name has to be used. - implemented NTLM to BASIC translation. (it gives only one try to enter credentials now) Verison 0.9.7 - Tue Dec 11 10:43:58 MSK 2001 - added ability to do multiple authentications in persistent connection. It looks like that there is some kind of NTLM extension that requires client to authenticate himself to every new network location even during persistent connection. - fixed bug when request is sent to currently connected web server even if it is a request to different network location. - 'Negotiate' authentication method name for www-authentication has been changed to 'NTLM' for compatibility with IIS/4.0 - added '-c' command line option to point to config file different than './server.cfg' - AUTH_DEBUG option added. It makes APS write detailed report on NTLM authentication process. - Highly experimental NTLM_FLAGS option allows changing NTLM flags in auth messages sent to remote server. Verison 0.9.5 - Tue Nov 6 03:02:53 MSK 2001 - UNICODE string support during authorization (DOMAIN, USER, HOST). - NT response is calculated now. Actually UNICODE and NT response are part of NTLM, but most of times everything works without them. See FULL_NTLM option in server.cfg - some performance improvement (I hope). - now it can behave as a standalone proxy server and do NTLM authentication at Web servers as well. While NTLM cannot pass through proxy server this option may be of use in intranet environment with athorization at local webservices. Just leave PARENT_PROXY option blank and it will connect to remote servers itself. Verison 0.9.1 - Fri May 25 01:51:30 MSD 2001 - if one side closed connection server tries sending data left in the buffer before closing connection to the other side. - new parameter in server.cfg - FRIENDLY_IPS that works in pair with ALLOW_EXTERNAL_CLIENTS:0. See server.cfg - a bit improved POST of long files, but not completely perfect. Verison 0.8.8 - Thu Apr 26 02:27:37 MSD 2001 - calls of socket.bind() and socket.connect() are compatible with Python >=1.6 now - ALLOW_EXTERNAL_CLIENTS parameter in server.cfg to allow/disallow clients from external hosts - NT password to use may be entered at the start time from the console (see server.cfg) - calmed down debug info sent to console. Now it appears only if DEBUG is set to 1 in server.cfg Verison 0.8.6 - Tue Apr 17 02:31:18 MSD 2001 - an ugly junkbuster's header delimiter '\012\012' worked around - hopefully fixed bug with leakage of MS Proxy info page into response with requested resource - value names are case insensitive now Verison 0.8.3 - Wed Apr 11 22:46:40 MSD 2001 - POST and PUT should work with NTLM - possibly there is a bug in proxy response header extracting routine Verison 0.8.2 - Sun Apr 8 02:16:41 MSD 2001 - Tunnel mode for HTTPS 'CONNECT' request - POST and PUT should work without authorization - fixed some bugs - POST does not work with NTLM authorization (working on this) Version 0.8.0 - Thu Mar 29 03:12:41 MSD 2001 - First public release. ntlmaps-0.9.9.0.1/__init__.py0000664000076400007640000000210310252206717015161 0ustar dixonddixond#!/usr/bin/python # This file is Copyright 2004 Darryl A. Dixon # and is part of 'NTLM Authorization Proxy Server', # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import os, sys ntlmaps_dir = os.path.dirname(os.path.abspath(__file__)) ntlmaps_libdir = ntlmaps_dir + '/lib' sys.path.append(ntlmaps_libdir) del os, sys ntlmaps-0.9.9.0.1/COPYING0000664000076400007640000004365310252205210014105 0ustar dixonddixond GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ntlmaps-0.9.9.0.1/research.txt0000664000076400007640000000143110252205210015373 0ustar dixonddixondSince release 0.9.7 APS has options related to authentication process. For now there are AUTH_DEBUG and NTLM_FLAGS available. AUTH_DEBUG:1 makes APS write detailed report on NTLM authentication dialog into *.auth files in APS' working directory. Actually not very useful but it may be helpful for understanding the NTLM authentication itself. NTLM_FLAGS: some_hex_digits forces APS to use custom NTLM flags during the authentication. For now it is known very little on their effects. So this option may be of use for better understanding of NTLM authentication method. NOTE: that flag format in NTLM_FLAGS is somewhat different from that in *.auth files reported with AUTH_DEBUG:1. NOTE 2: Looks like all this stuff will be useless without access to MS web or proxy server.ntlmaps-0.9.9.0.1/main.py0000775000076400007640000000302510366274304014360 0ustar dixonddixond#! /usr/bin/python # This file is part of 'NTLM Authorization Proxy Server' # Copyright 2001 Dmitry A. Rozmanov # # NTLM APS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # NTLM APS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the sofware; see the file COPYING. If not, write to the # Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # import __init__ import sys import server, config, config_affairs #-------------------------------------------------------------- # config affairs # look for default config name in lib/config.py conf = config.read_config(config.findConfigFileNameInArgv(sys.argv, __init__.ntlmaps_dir+'/')) conf['GENERAL']['VERSION'] = '0.9.9.0.1' #-------------------------------------------------------------- print 'NTLM authorization Proxy Server v%s' % conf['GENERAL']['VERSION'] print 'Copyright (C) 2001-2004 by Dmitry Rozmanov and others.' config = config_affairs.arrange(conf) #-------------------------------------------------------------- # let's run it serv = server.AuthProxyServer(config) serv.run() ntlmaps-0.9.9.0.1/readme.txt0000664000076400007640000001253410366274304015062 0ustar dixonddixondREADME file for 'NTLM Authorization Proxy Server' v.0.9.9.0.1 Release Purpose: ---------------- This is a bugfix release of the 0.9.9 stable series. See changelog.txt for full description. There is an issue with APS working as a standalone proxy. It serves requests from an http-client one by one and allows persistent connections, then it may receive several request in very sort time to one thread, and one of them may be to almost dead banner site, then all the requests made after that one will be waiting till that "bad" connection will be closed due to timeout. So I suggest switching off HTTP/1.1 presistent connections in your browser when you are using APS for web (not proxy) authentication and surfing banner rich evironment. This is a beta version, so I expect it to have bugs. If something goes wrong, please log a support request on the sourceforge.net project page. Please read this file to the end, it contains useful installation instructions and other information. 1. WHAT IS 'NTLM Authorization Proxy Server'? --------------------------------------------- 'NTLM Authorization Proxy Server' is a proxy-like software, that will authorize you at MS proxy server and at web servers (ISS especially) using MS proprietary NTLM authorization method and it can change some values in your client's request header so that those requests will look like ones made by MS IE. It is written in Python language. See www.python.org. 2. WHY MAY I WANT TO USE 'NTLM Authorization Proxy Server'? ----------------------------------------------------------- When your web browser uses proxy server to surf the Web from within your local network your may be required to authenticate youself to use proxy. There are two methods to do this: using 'Basic' authorization method and 'Digest' method. 'Basic' is very basic;) and not secure, but considered often as sufficient to check users inside your local network. The second - 'Digest' - is quite secure but, as I could note, use of it considered as 'too much' in order to protect your own proxy server from your own people. Well, 'Digest' is far more complicate method and I don't know any software that can use it, I think it is becuse this method simply 'overkill' for most cases. 'Basic' method is quite common and is standard method to authenticate yourself at proxies. But MicroSoft, being MS, has developed instead of standard methods two its own authentication methods - 'NTLM' (NT LanManager) and 'Negotiate'. I don't know anything about the second one, but I spend some time on 'NTLM'. Because NTLM (NT LanManager) is a proprietary algorithm it is looks like that only MS's products can use it (IE3+ as far as I know). And it gives you a bunch of fun things: If proxy server that you have to use is set so it requres your to authenticate yourself with 'NTLM' algorithm then you will be able to use ONLY MS's products with this proxy server. No ReGet, no WGET, no Junkbuster, no Netscape Navigator, no Lynx or your local squid and no lots of other good software. Why admin of proxy server in your local network may want to ban standard 'Basic' and require 'NTLM'? I don't know. May be because he likes MS too much;). 3. HISTORY BEHIND THE 'NTLM Authorization Proxy Server'. -------------------------------------------------------- I got the idea for 'NTLM Authorization Proxy Server' when admin in our network swiched MS Proxy so that we had to use 'NTLM'. It was simply disaster. Everything stopped working but IE5.5. So then I had realized that there was no solution to make my favorite programs work with it I decided to do something. And here you are. 4. REQUIREMENTS --------------- You will need a thread-safe Python installation, version 1.5.2 or later. Python is available from http://www.python.org/ . Get it, install it, and you are ready to try. Python also comes with all GNU/Linux distributions that I am aware of, so if you are a GNU/Linux user you should have that in your box already. Please note that I have not test it with Python versions higher than 1.5.2. Therefore you may have problems with later Pythons, but I hope you will not. 5. LICENSING & PRICING ---------------------- 'NTLM Authorization Proxy Server' is distributed under the GNU Public License, which is included in this archive (see file COPYING). The above mean that 'NTLM Authorization Proxy Server' is pretty much free. You have to pay nothing for it. 6. CREDITS ---------- Thanks to Nathan Lineback for reporting about persistent connection to MS server with multiple authorization requests. I had never heard of such a behavior before that. Janek Schwarz added command line option '-c' for config files other than default server.cfg in working directory. Thanks for that to him. Thanks to Stephen D. Cohen for very useful suggestions and cooperation. Patch for Python >=1.6 by Markus Indenbirken (markus_i@gmx.de) The most useful info has been got from "NTLM Authentication Scheme for HTTP" page by Ronald Tschalar (ronald@innovation.ch). I included the page in the distribution because it looks like that the server it contains is going down. (http://www.innovation.ch/java/ntlm.html) md4.py and des_c.py had been translated from md4.c and des.c from "the Python Cryptography Toolkit, version 1.0.0 Copyright (C) 1995, A.M. Kuchling"