cntlm-0.92.3/0000755000175000017500000000000011746523240011666 5ustar daviddavidcntlm-0.92.3/http.h0000664000175000017500000000347111665203371013025 0ustar daviddavid/* * HTTP handling routines and related socket stuff for CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _HTTP_H #define _HTTP_H #include #include "utils.h" #include "auth.h" /* * A couple of shortcuts for if statements */ #define CONNECT(data) ((data) && (data)->req && !strcasecmp("CONNECT", (data)->method)) #define HEAD(data) ((data) && (data)->req && !strcasecmp("HEAD", (data)->method)) #define GET(data) ((data) && (data)->req && !strcasecmp("GET", (data)->method)) typedef long long int length_t; extern int is_http_header(const char *src); extern char *get_http_header_name(const char *src); extern char *get_http_header_value(const char *src); extern int http_parse_basic(hlist_t headers, const char *header, struct auth_s *tcreds); extern int headers_recv(int fd, rr_data_t data); extern int headers_send(int fd, rr_data_t data); extern int tunnel(int cd, int sd); extern length_t http_has_body(rr_data_t request, rr_data_t response); extern int http_body_send(int writefd, int readfd, rr_data_t request, rr_data_t response); extern int http_body_drop(int fd, rr_data_t response); #endif /* _HTTP_H */ cntlm-0.92.3/pages.c0000664000175000017500000000502011670713123013125 0ustar daviddavid/* * Static HTML page generators for CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _PAGES_H #define _PAGES_H #include "utils.h" #include "string.h" #include "stdio.h" char *gen_407_page(const char *http) { char *tmp; if (http == NULL) http = "HTTP/1.0"; tmp = new(BUFSIZE); snprintf(tmp, BUFSIZE-1, "%s 407 Access denied\r\n" "Proxy-Authenticate: Basic realm=\"Cntlm Proxy\"\r\n" "Content-Type: text/html\r\n\r\n" "

407 Access denied

Cntlm requests your credentials for proxy access.

", http); return tmp; } char *gen_401_page(const char *http, const char *host, int port) { char *tmp; if (http == NULL) http = "HTTP/1.0"; tmp = new(BUFSIZE); snprintf(tmp, BUFSIZE-1, "%s 401 Access denied\r\n" "WWW-Authenticate: Basic realm=\"%s:%d\"\r\n" "Content-Type: text/html\r\n\r\n" "

401 Access denied

Cntlm proxy requests your credentials for this URL.

", http, host, port); return tmp; } char *gen_denied_page(const char *ip) { char *tmp; if (ip == NULL) ip = "client"; tmp = new(BUFSIZE); snprintf(tmp, BUFSIZE-1, "HTTP/1.0 407 Access denied\r\n" "Content-Type: text/html\r\n\r\n" "

Access denied

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

", ip); return tmp; } char *gen_502_page(const char *http, const char *msg) { char *tmp; if (http == NULL) http = "HTTP/1.0"; if (msg == NULL) msg = "Proxy error"; tmp = new(BUFSIZE); snprintf(tmp, BUFSIZE-1, "%s 502 %s\r\n" "Content-Type: text/html\r\n\r\n" "

502 %s

Cntlm proxy failed to complete the request.

", http, msg, msg); return tmp; } #endif /* _PAGES_H */ cntlm-0.92.3/doc/0000755000175000017500000000000011725657003012435 5ustar daviddavidcntlm-0.92.3/doc/cntlm.10000664000175000017500000006472211714512231013640 0ustar daviddavid.TH CNTLM 1 "Nov 2010" "cntlm 0.90" "Accelerating NTLM/NTLMv2 Authentication Proxy" .SH NAME \fBcntlm\fP - authenticating HTTP(S) proxy with TCP/IP tunneling and acceleration .SH SYNOPSIS .B cntlm [ .B -AaBcDdFfgHhILlMPprSsTUuvw ] [ \fIhost1\fP \fIport1\fP | \fIhost1\fP:\fIport1\fP ] ... \fIhostN\fP \fIportN\fP .SH DESCRIPTION \fBCntlm\fP is an NTLM/NTLM SR/NTLMv2 authenticating HTTP proxy. It stands between your applications and the corporate proxy, adding NTLM authentication on-the-fly. You can specify several "parent" proxies and Cntlm will try one after another until one works. All auth'd connections are cached and reused to achieve high efficiency. Just point your apps proxy settings at Cntlm, fill in cntlm.conf (cntlm.ini) and you're ready to do. This is useful on Windows, but essential for non-Microsoft OS's. Proxy IP addresses can be specified via CLI (\fIhost1:port1\fP to \fIhostN:portN\fP) or the configuration file. Another option is to have \fBcntlm\fP authenticate your local web connections without any parent proxies. It can work in a stand-alone mode, just like Squid or ISA. By default, all requests are forwarded to parent proxies, but the user can set a "NoProxy" list, a list of URL matching wild-card patterns, that route between direct and forward modes. \fBCntlm\fP can also recognize when all your corporate proxies are unavailable and switch to stand-alone mode automatically (and then back again). Aside from \fIWWW\fP and \fIPROXY\fP authentication, \fBcntlm\fP provides a useful feature enabling users migrate their laptops between work and home without changing proxy settings in their applications (using \fBcntlm\fP all the time). \fBCntlm\fP also integrates transparent TCP/IP port forwarding (tunneling). Each tunnel opens a new listening socket on local machine and and forwards all connections to the target host behind the parent proxy. Instead of these SSH-like tunnels, user can also choose a limited SOCKS5 interface. .PP Core \fBcntlm\fP function had been similar to the late NTLMAPS, but today, \fBcntlm\fP has evolved way beyond anything any other application of this type can offer. The feature list below speaks for itself. \fBCntlm\fP has many security/privacy features like \fBNTLMv2\fP support and password protection - it is possible to substitute password hashes (which can be obtained using\ \fB-H\fP) in place of the actual password or to enter the password interactively (on start-up or via "basic" HTTP auth translation). If plaintext password is used, it is automatically hashed during the startup and all traces of it are removed from the process memory. .PP In addition to minimal use of system resources, \fBcntlm\fP achieves higher throughput on a given link. By caching authenticated connections, it acts as an HTTP accelerator; This way, the 5-way auth handshake for each connection is transparently eliminated, providing immediate access most of the time. \fBCntlm\fP never caches a request/reply body in memory, in fact, no traffic is generated except for the exchange of auth headers until the client <-> server connection is fully negotiated. Only then real data transfer takes place. \fBCntlm\fP is written in optimized C and easily achieves fifteen times faster responses than others. .PP An example of \fBcntlm\fP compared to NTLMAPS: \fBcntlm\fP gave avg 76 kB/s with peak CPU usage of 0.3% whereas with NTLMAPS it was avg 48 kB/s with peak CPU at 98% (Pentium M 1.8 GHz). The extreme difference in resource usage is one of many important benefits for laptop use. Peak memory consumption (several complex sites, 50 paralell connections/threads; values are in KiB): .nf .ft C VSZ RSS CMD 3204 1436 ./cntlm \-f \-c ./cntlm.conf \-P pid 411604 6264 /usr/share/ntlmaps/main.py \-c /etc/ntlmaps/server.cfg .ft P .fi .ne 6 .PP Inherent part of the development is profiling and memory management screening using Valgrind. The source distribution contains a file called \fIvalgrind.txt\fP, where you can see the report confirming zero leaks, no access to unallocated memory, no usage of uninitialized data - all traced down to each instruction emulated in Valgrind's virtual CPU during a typical production lifetime of the proxy. .SH OPTIONS Most options can be pre-set in a configuration file. Specifying an option more than once is not an error, but \fBcntlm\fP ignores all occurences except the last one. This does not apply to options like\ \fB-L\fP, each of which creates a new instance of some feature. \fBCntlm\fP can be built with a hardcoded configuration file (e.g. /etc/cntlm.conf), which is always loaded, if possible. See\ \fB-c\fP option on how to override some or all of its settings. Use \fB-h\fP to see available options with short description. .TP .B -A IP/mask\ \ \ \ (Allow) Allow ACL rule. Together with \fB-D\fP (Deny) they are the two rules allowed in ACL policy. It is more usual to have this in a configuration file, but \fBCntlm\fP follows the premise that you can do the same on the command-line as you can using the config file. When \fBCntlm\fP receives a connection request, it decides whether to allow or deny it. All ACL rules are stored in a list in the same order as specified. \fBCntlm\fP then walks the list and the first \fIIP/mask\fP rule that matches the request source address is applied. The \fImask\fP can be any number from 0 to 32, where 32 is the default (that is exact IP match). This notation is also known as CIDR. If you want to match everything, use \fB0/0\fP or an asterix. ACLs on the command-line take precedence over those in the config file. In such case, you will see info about that in the log (among the list of unused options). There you can also see warnings about possibly incorrect subnet spec, that's when the \fIIP\fP part has more bits than you declare by \fImask\fP (e.g. 10.20.30.40/24 should be 10.20.30.0/24). .TP .B -a NTLMv2 | NTLM2SR | NT | NTLM | LM\ \ \ \ (Auth) Authentication type. NTLM(v2) comprises of one or two hashed responses, NT and LM or NTLM2SR or NTv2 and LMv2, which are computed from the password hash. Each response uses a different hashing algorithm; as new response types were invented, stronger algorithms were used. When you first install \fBcntlm\fP, find the strongest one which works for you (preferably using\ \fB-M\fP). Above they are listed from strongest to weakest. Very old servers or dedicated HW proxies might be unable to process anything but LM. If none of those work, see compatibility flags option\ \fB-F\fP or submit a Support Request. .br \fBIMPORTANT:\fP Although NTLMv2 is not widely adopted (i.e. enforced), it is supported on all Windows since NT\ 4.0\ SP4. That's for \fBa very long time\fP! I strongly suggest you use it to protect your credentials on-line. You should also replace plaintext \fBPassword\fP options with hashed \fBPass[NTLMv2|NT|LM]\fP equivalents. NTLMv2 is the most and possibly the only secure authentication of the NTLM family. .ne 4 .TP .B -B\ \ \ \ (NTLMToBasic) This option enables "NTLM-to-basic", which allows you to use one \fBcntlm\fP for multiple users. Please note that all security of NTLM is lost this way. Basic auth uses just a simple encoding algorithm to "hide" your credentials and it is moderately easy to sniff them. .ne 6 IMPORTANT: HTTP protocol obviously has means to negotiate authorization before letting you through, but TCP/IP doesn't (i.e. open port is open port). If you use NTLM-to-basic and DON'T specify some username/password in the configuration file, you are bound to loose tunneling features, because \fBcntlm\fP alone won't know your credentials. Because NTLM identification has at least three parts (username, password, domain) and the basic authentication provides fields for only two (username, password), you have to smuggle the domain part somewhere. You can set the \fBDomain\fP config/cmd-line parameter, which will then be used for all users, who don't specify their domain as a part of the username. To do that and override the global domain setting, use this instead of plain username in the password dialog: "domain\\username". .TP .B -c Configuration file. Command-line options, if used, override its single options or are added at the top of the list for multi options (tunnels, parent proxies, etc) with the exception of ACLs, which are completely overriden. Use \fI/dev/null\fP to disable any config file. .TP .B -D IP/mask\ \ \ \ (Deny) Deny ACL rule. See option \fB-A\fP above. .TP .B -d \ \ \ \ (Domain) The domain or workgroup of the proxy account. This value can also be specified as a part of the username with \fB-u\fP. .TP .B -F \ \ \ \ (Flags) NTLM authentication flags. This option is rater delicate and I do not recommend to change the default built-in values unless you had no success with parent proxy auth and tried magic autodetection (\fB-M\fP) and all possible values for the \fBAuth\fP option (\fB-a\fP). Remember that each NT/LM hash combination requires different flags. This option is sort of a complete "manual override" and you'll have to deal with it yourself. .ne 5 .TP .B -f Run in console as a foreground job, do not fork into background. In this mode, all syslog messages will be echoed to the console (on platforms which support syslog LOG_PERROR option). Though \fBcntlm\fP is primarily designed as a classic UNIX daemon with syslogd logging, it provides detailed verbose mode without detaching from the controlling terminal; see \fB-v\fP. In any case, all error and diagnostic messages are always sent to the system logger. .ne 6 .TP .B -G \ \ \ \ (ISAScannerAgent) User-Agent matching (case insensitive) for trans-isa-scan plugin (see \fB-S\fP for explanation). Positive match identifies requests (applications) for which the plugin should be enabled without considering the size of the download (see \fB-S\fP). You can use shell wildcard characters, namely "*", "?" and "[]". If used without \fB-S\fP or \fBISAScannerSize\fP, the \fImax_size_in_kb\fP is internally set to infinity, so the plugin will be active ONLY for selected User-Agents, regardless of download size. .ne 6 .TP .B -g\ \ \ \ (Gateway) Gateway mode, \fBcntlm\fP listens on all network interfaces. Default is to bind just loopback. That way, only local processes can connect to \fBcntlm\fP. In the gateway mode though, \fBcntlm\fP listens on all interfaces and is accessible to other machines on the network. Please note that with this option the command-line order matters when specifying proxy or tunnel local (listening) ports. Those positioned before it will bind only loopback; those after will be public. .br IMPORTANT: All of the above applies only to local ports for which you didn't specify any source address. If you did, \fBcntlm\fP tries to bind the given port only on the specified interface (or rather IP address). .TP .B -H Use this option to get hashes for password-less configuration. In this mode, \fBcntlm\fP prints the results and exits. You can just copy & paste right into the config file. You ought to use this option with explicit \fB-u\fP and \fB-d\fP, because some hashes include the username and domain name in the calculation. Do see \fB-a\fP for security recommendations. .TP .B -h Display help (available options with a short description) and exit. .TP .B -I Interactive password prompt. Any password settings from the command line or config file is ignored and a password prompt is issued. Use this option only from shell. .TP .B -L [:]::\ \ \ \ (Tunnel) Tunnel definition. The syntax is the same as in OpenSSH's local forwarding (\fB-L\fP), with a new optional prefix, \fIsaddr\fP - the source IP address to bind the \fIlport\fP to. \fBCntlm\fP will listen for incomming connections on the local port \fIlport\fP, forwarding every new connection through the parent proxy to the \fIrhost\fP:\fIrport\fP (authenticating on the go). This option can be used multiple times for unlimited number of tunnels, with or without the \fIsaddr\fP option. See \fB-g\fP for the details concerning local port binding when \fIsaddr\fP is not used. Please note that many corporate proxies do not allow connections to ports other than 443 (https), but if you run your target service on this port, you should be safe. Connect to HTTPS is "always" allowed, otherwise nobody would be able to browse https:// sites. In any case, first try if you can establish a connection through the tunnel, before you rely on it. This feature does the same job as tools like \fBcorkscrew(1)\fP, but instead of communicating over a terminal, \fBcntlm\fP keeps it TCP/IP. .ne 5 .TP .B -l [:]\ \ \ \ (Listen) Local port for the \fBcntlm\fP proxy service. Use the number you have chosen here and the hostname of the machine running \fBcntlm\fP (possibly localhost) as proxy settings in your browser and/or the environment. Most applications (including console) support the notion of proxy to connect to other hosts. On POSIX, set the following variables to use e.g. \fBwget(1)\fP without any trouble (fill in the actual address of \fBcntlm\fP): .nf .ft C $ export ftp_proxy=http://localhost:3128 $ export http_proxy=$ftp_proxy $ export https_proxy=$ftp_proxy .ft P .fi .ne 6 You can choose to run the proxy service on more than one port, in such case just use this option as many times as necessary. But unlike tunnel definition, \fBcntlm\fP fails to start if it cannot bind all of the proxy service ports. Proxy service port can also be bound selectively. Use \fIsaddr\fP to pick source IP address to bind the \fIlport\fP to. This allows you, for example, to run the service on different ports for subnet A and B and make it invisible for subnet C. See \fB-g\fP for the details concerning local port binding when \fIsaddr\fP is not used. .TP .B -M Run magic NTLM dialect detection. In this mode, \fBcntlm\fP tries some known working presets against your proxy. Probe requests are made for the specified \fItesturl\fP, with the strongest hashes going first. When finished, settings for the most secure setup are printed. Although the detection will tell you which and how to use \fBAuth\fP, \fBFlags\fP and password-hash options, you have to configure at least your credentials and proxy address first. You can use \fB-I\fP to enter your password interactively. .ne 5 .TP .B -N [,:]\ \ \ \ (SOCKS5Proxy) Enable SOCKS5 proxy and make it listen on local port \fIport_number\fP (source IP spec is also possible, as with all options). By default, there will be no restrictions as to who can use this service. Some clients don't even support SOCKS5 authentication (e.g. almost all browsers). If you wish to enforce authentication, use \fB-R\fP or its equivalent option, \fBSOCKS5User\fP. As with port tunneling, it is up to the parent proxy whether it will allow connection to any requested host:port. This feature can be used with \fBtsocks(1)\fP to make most TCP/IP applications go thru the proxy rather than directly (only outgoing connections will work, obviously). To make apps work without DNS server, it is important that they don't resolve themselves, but using SOCKS. E.g. Firefox has this option available through URI "about:config", key name \fBnetwork.proxy.socks_remote_dns\fP, which must be set to \fBtrue\fP. Proxy-unaware \fBtsocks\fPified apps, will have to be configured using IP addresses to prevent them from DNS resolving. .ne 5 .TP .B -P Create a PID file \fIpidfile\fP upon startup. If the specified file exists, it is truncated and overwritten. This option is intended for use with \fBstart-stop-daemon(8)\fP and other servicing mechanisms. Please note that the PID file is created AFTER the process drops its privileges and forks. When the daemon finishes cleanly, the file is removed. .ne 5 .TP .B -p \ \ \ \ (Password, PassNT, ...) Proxy account password. \fBCntlm\fP deletes the password from the memory, to make it invisible in /proc or with inspection tools like \fBps(1)\fP, but the preferable way of setting password is the configuration file. To that end, you can use \fBPassword\fP option (for plaintext, human readable format), or "encrypt" your password via \fB-H\fP and then use \fBPassNTLMv2\fP, \fBPassNT\fP and/or \fBPassLM\fP. .ne 3 .TP .B -R :\ \ \ \ (SOCKS5User) If SOCKS5 proxy is enabled, this option can make it accessible only to those who have been authorized. It can be used several times, to create a whole list of accounts (allowed user:pass combinations). .TP .B -S \ \ \ \ (ISAScannerSize) Enables the plugin for transparent handling of the dreaded ISA AV scanner, which returns an interactive HTTP page (displaying the scanning progress) instead of the file/data you've requested, every time it feels like scanning the contents. This presumptuous behavior breaks every automated downloader, updater and basically EVERY application relying on downloads (e.g. wget, apt-get). .ne 6 The parameter \fImax_size_in_kb\fP allows you to choose maximum download size you wish to handle by the plugin (see below why you might want that). If the file size is bigger than this, \fBcntlm\fP forwards you the interactive page, effectively disabling the plugin for that download. Zero means no limit. Use \fB-G\fP/\fBISAScannerAgent\fP to identify applications for which \fImax_size_in_kb\fP should be ignored (forcing the plugin). It works by matching User-Agent header and is necessary for e.g. wget, apt-get and yum, which would fail if the response is some HTTP page instead of requested data. .ne 8 How it works: the client asks for a file, \fBcntlm\fP detects ISA's bullshit response and waits for the secret link to ISA's cache, which comes no sooner than the file is downloaded and scanned by ISA. Only then can \fBcntlm\fP make the second request for the real file and forward it along with correct headers to the client. The client doesn't timeout while waiting for it, b/c \fBcntlm\fP is periodically sending an extra "keepalive" header, but the user might get nervous not seeing the progress bar move. It's of course \fBpurely psychological\fP matter, there's no difference if \fBcntlm\fP or your browser requests the scanned file - you must wait for ISA to do it's job and download then. You just expect to see some progress indicator move, which is all what the ISA's page does: it shows HTML countdown. .ne 2 If the plugin cannot parse the interactive page for some reason (unknown formatting, etc.), it quits and the page is forwarded to you - it's never "lost". .ne 6 The keepalive header is called \fCISA-Scanner\fP and shows ISA's progress, e.g.: .nf .ft C HTTP/1.1 200 OK ISA-Scanner: 1000 of 10000 ISA-Scanner: 2000 of 10000 ... .ft P .fi .TP .B -r \fB": "\fP\ \ \ \ (Header) Header substitution. Every client's request will be processed and any headers defined using \fB-r\fP or in the configuration file will be added to it. In case the header is already present, its value will be replaced. .TP .B -s Serializes all requests by not using concurrent threads for proxy (tunneling still works in parallel). This has a horrible impact on performance and is available only for debugging purposes. When used with \fB-v\fP, it yields nice sequential debug log, where requests take turns. .TP .B -T Used in combination with \fB-v\fP to save the debug output into a trace file. It should be placed as the first parameter on the command line. To prevent data loss, it never overwrites an existing file. You have to pick a unique name or manually delete the old file. .ne 7 .TP .B -U When executed as root, do the stuff that needs such permissions (read config, bind ports, etc.) and then immediately drop privileges and change to \fIuid\fP. This parameter can be either number or system username. If you use a number, both uid and gid of the process will be set to this value; if you specify a username, uid and gid will be set according to that user's uid and primary gid as defined in \fI/etc/passwd\fP. You should use the latter, possibly using a dedicated \fBcntlm\fP account. As with any daemon, you are \fBstrongly\fP advised to run \fBcntlm\fP under a non-privileged account. .TP .B -u [@]\ \ \ \ (Username) Proxy account/user name. Domain can be be entered as well. .TP .B -v Print debugging information. Automatically enables (\fB-f\fP). .TP .B -w \ \ \ \ (Workstation) Workstation NetBIOS name. Do not use full qualified domain name (FQDN) here. Just the first part. If not specified, \fBcntlm\fP tries to get the system hostname and if that fails, uses "cntlm" - it's because some proxies require this field non-empty. .SH CONFIGURATION Configuration file is basically an INI file, except there are no "=" between keys and values. It comprises of whitespace delimited keyword and value pairs. Apart from that, there are sections as well, they have the usual "[section_name]" syntax. Comment begins with a hash "#" or a semicolon ";" and can be anywhere in the file. Everything after the mark up until EOL is a comment. Values can contain any characters, including whitespace. You \fIcan\fP use double quotes around the value to set a string containing special characters like spaces, pound signs, etc. No escape sequences are allowed in quoted strings. There are two types of keywords, \fIlocal\fP and \fIglobal\fP. Local options specify authentication details per domain (or location). Global keywords apply to all sections and proxies. They should be placed before all sections, but it's not necessary. They are: \fCAllow, Deny, Gateway, Listen, SOCKS5Proxy, SOCKS5User, NTLMToBasic, Tunnel\fP. All available keywords are listed here, full descriptions are in the OPTIONS section: .TP .B Allow [/] ACL allow rule, see \fB-A\fP. .TP .B Auth NTLMv2 | NTLM2SR | NT | NTLM | LM Select any possible combination of NTLM hashes using a single parameter. .TP .B Deny [/] ACL deny rule, see \fB-A\fP. .TP .B Domain Proxy account domain/workgroup name. .TP .B Flags NTLM authentication flags. See \fB-F\fP for details. .TP .B Gateway yes|no Gateway mode. In the configuration file, order doesn't matter. Gateway mode applies the same to all tunnels. .TP .B Header Header substitution. See \fB-r\fP for details and remember, no quoting. .ne 4 .TP .B ISAScannerAgent Wildcard-enabled (*, ?, []) case insensitive User-Agent string matching for the trans-isa-plugin. If you don't define \fBISAScannerSize\fP, it is internally set to infinity, i.e. disabling the plugin for all downloads except those agent-matched ones. See \fB-G\fP. .ne 2 .TP .B ISAScannerSize Enable trans-isa-scan plugin. See \fB-S\fP for more. .ne 2 .TP .B Listen [:] Local port number for the \fBcntlm\fP's proxy service. See \fB-l\fP for more. .TP .B Password Proxy account password. As with any other option, the value (password) can be enclosed in double quotes (") in case it contains special characters like spaces, pound signs, etc. .ne 11 .TP .B PassNTLMv2, PassNT, PassLM Hashes of the proxy account password (see \fB-H\fP and \fB-a\fP). When you want to use hashes in the config (instead of plaintext password), each \fBAuth\fP settings requires different options: .nf .ft C Settings | Requires -------------+----------------- Auth NTLMv2 | PassNTLMv2 Auth NTLM2SR | PassNT Auth NT | PassNT Auth NTLM | PassNT + PassLM Auth LM | PassLM .ft P .fi .TP .B Proxy Parent proxy, which requires authentication. The same as proxy on the command-line, can be used more than once to specify an arbitrary number of proxies. Should one proxy fail, \fBcntlm\fP automatically moves on to the next one. The connect request fails only if the whole list of proxies is scanned and (for each request) and found to be invalid. Command-line takes precedence over the configuration file. .TP .B NoProxy , , ... Avoid parent proxy for these host names. All matching URL's will be proxied \fIdirectly\fP by \fBcntlm\fP as a stand-alone proxy. \fBCntlm\fP supports WWW authentication in this mode, thus allowing you to access local intranet sites with corporate NTLM authentication. Hopefully, you won't need that virtualized MSIE any more. :) See \fB-N\fP for more. .TP .B SOCKS5Proxy [:] Enable SOCKS5 proxy. See \fB-O\fP for more. .TP .B SOCKS5User : Create a new SOCKS5 proxy account. See \fB-R\fP for more. .TP .B NTLMToBasic yes|no Enable/disable NTLM-to-basic authenticatoin. See \fB-B\fP for more. .TP .B Tunnel [:]:: Tunnel definition. See \fB-L\fP for more. .TP .B Username Proxy account name, without the possibility to include domain name ('at' sign is interpreted literally). .TP .B Workstation The hostname of your workstation. .ne 7 .SH FILES The optional location of the configuration file is defined in the Makefile, with the default for 1) deb/rpm package, 2) traditional "make; make install" and 3) Windows installer, respectively, being: .nf .ft C 1) /etc/cntlm.conf 2) /usr/local/etc/cntlm.conf 3) %PROGRAMFILES%\\Cntlm\\cntlm.ini .ft P .fi .SH PORTING \fBCntlm\fP is being used on many platforms, little and big endian machines, so users should not have any problems with compilation. Nowadays, \fBcntlm\fP is a standard tool in most Linux distributions and there are various repositories for other UNIX-like systems. Personally, I release Debian Linux (deb), RedHat Linux (rpm) and Windows (exe) binaries, but most people get \fBcntlm\fP from their OS distributor. .ne 2 For compilation details, see README in the source distribution. Porting to any POSIX conforming OS shouldn't be more than a matter of a Makefile rearrangement. \fBCntlm\fP uses strictly POSIX.1-2001 interfaces with ISO C99 libc and is also compliant with SUSv3. Since version 0.33, \fBcntlm\fP supports Windows using a POSIX emulation layer called \fBCygwin\fP. .SH BUGS \fBTo report a bug\fP, enable the debug output, save it to a file and submit on-line along with a detailed description of the problem and how to reproduce it. Visit the home page for more. .nf .ft C cntlm \-T cntlmtrace.log \-v \-s ... the rest ... .ft P .fi .SH AUTHOR Written by David Kubicek .br Homepage: http://cntlm.sourceforge.net/ .SH COPYRIGHT Copyright \(co 2007-2010 David Kubicek .br \fBCntlm\fP uses DES, MD4, MD5 and HMAC-MD5 routines from \fBgnulib\fP and Base64 routines from \fBmutt(1)\fP. cntlm-0.92.3/doc/cntlm.conf0000664000175000017500000000541311351636635014431 0ustar daviddavid# # Cntlm Authentication Proxy Configuration # # NOTE: all values are parsed literally, do NOT escape spaces, # do not quote. Use 0600 perms if you use plaintext password. # Username testuser Domain corp-uk Password password # NOTE: Use plaintext password only at your own risk # Use hashes instead. You can use a "cntlm -M" and "cntlm -H" # command sequence to get the right config for your environment. # See cntlm man page # Example secure config shown below. # PassLM 1AD35398BE6565DDB5C4EF70C0593492 # PassNT 77B9081511704EE852F94227CF48A793 ### Only for user 'testuser', domain 'corp-uk' # PassNTLMv2 D5826E9C665C37C80B53397D5C07BBCB # Specify the netbios hostname cntlm will send to the parent # proxies. Normally the value is auto-guessed. # # Workstation netbios_hostname # List of parent proxies to use. More proxies can be defined # one per line in format : # Proxy 10.0.0.41:8080 Proxy 10.0.0.42:8080 # List addresses you do not want to pass to parent proxies # * and ? wildcards can be used # NoProxy localhost, 127.0.0.*, 10.*, 192.168.* # Specify the port cntlm will listen on # You can bind cntlm to specific interface by specifying # the appropriate IP address also in format : # Cntlm listens on 127.0.0.1:3128 by default # Listen 3128 # If you wish to use the SOCKS5 proxy feature as well, uncomment # the following option. It can be used several times # to have SOCKS5 on more than one port or on different network # interfaces (specify explicit source address for that). # # WARNING: The service accepts all requests, unless you use # SOCKS5User and make authentication mandatory. SOCKS5User # can be used repeatedly for a whole bunch of individual accounts. # #SOCKS5Proxy 8010 #SOCKS5User dave:password # Use -M first to detect the best NTLM settings for your proxy. # Default is to use the only secure hash, NTLMv2, but it is not # as available as the older stuff. # # This example is the most universal setup known to man, but it # uses the weakest hash ever. I won't have it's usage on my # conscience. :) Really, try -M first. # #Auth LM #Flags 0x06820000 # Enable to allow access from other computers # #Gateway yes # Useful in Gateway mode to allow/restrict certain IPs # Specifiy individual IPs or subnets one rule per line. # #Allow 127.0.0.1 #Deny 0/0 # GFI WebMonitor-handling plugin parameters, disabled by default # #ISAScannerSize 1024 #ISAScannerAgent Wget/ #ISAScannerAgent APT-HTTP/ #ISAScannerAgent Yum/ # Headers which should be replaced if present in the request # #Header User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98) # Tunnels mapping local port to a machine behind the proxy. # The format is :: # #Tunnel 11443:remote.com:443 cntlm-0.92.3/doc/valgrind.txt0000664000175000017500000000547210662100117015002 0ustar daviddavidMemory leaks ~~~~~~~~~~~~ This is Valgrind report on memory usage after some heavy browsing (60 parallel connections) and daemon termination. ;) ==741== malloc/free: in use at exit: 0 bytes in 0 blocks. ==741== malloc/free: 2,049 allocs, 2,049 frees, 197,861 bytes allocated. ==741== ==741== All heap blocks were freed -- no leaks are possible. --741-- memcheck: sanity checks: 6 cheap, 1 expensive --741-- memcheck: auxmaps: 0 auxmap entries (0k, 0M) in use --741-- memcheck: auxmaps: 0 searches, 0 comparisons --741-- memcheck: SMs: n_issued = 29 (464k, 0M) --741-- memcheck: SMs: n_deissued = 6 (96k, 0M) --741-- memcheck: SMs: max_noaccess = 65535 (1048560k, 1023M) --741-- memcheck: SMs: max_undefined = 0 (0k, 0M) --741-- memcheck: SMs: max_defined = 782 (12512k, 12M) --741-- memcheck: SMs: max_non_DSM = 28 (448k, 0M) --741-- memcheck: max sec V bit nodes: 1 (0k, 0M) --741-- memcheck: set_sec_vbits8 calls: 1 (new: 1, updates: 0) --741-- memcheck: max shadow mem size: 752k, 0M --741-- translate: fast SP updates identified: 4,916 ( 90.1%) --741-- translate: generic_known SP updates identified: 342 ( 6.2%) --741-- translate: generic_unknown SP updates identified: 194 ( 3.5%) --741-- tt/tc: 9,734 tt lookups requiring 10,056 probes --741-- tt/tc: 9,734 fast-cache updates, 4 flushes --741-- transtab: new 4,484 (100,307 -> 1,609,941; ratio 160:10) [0 scs] --741-- transtab: dumped 0 (0 -> ??) --741-- transtab: discarded 133 (2,418 -> ??) --741-- scheduler: 1,103,546 jumps (bb entries). --741-- scheduler: 6/39,639 major/minor sched events. --741-- sanity: 7 cheap, 1 expensive checks. --741-- exectx: 30,011 lists, 221 contexts (avg 0 per list) --741-- exectx: 4,115 searches, 3,894 full compares (946 per 1000) --741-- exectx: 0 cmp2, 60 cmp4, 0 cmpAll Memory usage ~~~~~~~~~~~~ Heap allocation per function can be seen in memory_consumption_with_reqex.pdf. It is apparent that in cntlm, regular expression matching accounts for more that 60% of allocated memory. I don't like that - regex is used for parsing the option -L at startup and later in header manipulation, once for each request to parse the first HTTP line. Regex was used there as a convenient and transparent means to split and partly verify strings, but I'm not willing to pay for it 1.5 times as much memory as the application would need without it. Ok, now I have made it optional to use reqex - using NTLM_REGEX. Without this #define, everything is parsed by plain C memory manipulation. The graph in memory_consumption_without_reqex.pdf shows significant drop in memory usage, overall less than a half, actually. Both test were done by an automated test with 60 connections in parallel and the same sites fully loaded. Regexes have been removed for good. No point in keeping around old unnecessary code. cntlm-0.92.3/COPYRIGHT0000664000175000017500000000160611350715406013164 0ustar daviddavid CNTLM - Authenticating HTTP Proxy Copyright (C) 2007-2010 David Kubicek 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 If you would like to negotiate alternate licensing terms, you may do so by contacting: David Kubicek , cntlm-0.92.3/http.c0000664000175000017500000003311511670713123013013 0ustar daviddavid/* * HTTP handling routines and related socket stuff for CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "socket.h" #include "ntlm.h" #include "http.h" #define BLOCK 2048 extern int debug; /* * Ture if src is a header. This is just a basic check * for the colon delimiter. Might eventually become more * sophisticated. :) */ int is_http_header(const char *src) { return strcspn(src, ":") != strlen(src); } /* * Extract the header name from the source. */ char *get_http_header_name(const char *src) { int i; i = strcspn(src, ":"); if (i != strlen(src)) return substr(src, 0, i); else return NULL; } /* * Extract the header value from the source. */ char *get_http_header_value(const char *src) { char *sub; if ((sub = strchr(src, ':'))) { sub++; while (*sub == ' ') sub++; return strdup(sub); } else return NULL; } /* * Receive HTTP request/response from the given socket. Fill in pre-allocated * rr_data_t structure. * Returns: 1 if OK, 0 in case of socket EOF or other error */ int headers_recv(int fd, rr_data_t data) { int i, bsize; int len; char *buf; char *tok, *s3 = 0; char *orig = NULL; char *ccode = NULL; char *host = NULL; bsize = BUFSIZE; buf = new(bsize); i = so_recvln(fd, &buf, &bsize); if (i <= 0) goto bailout; if (debug) printf("HEAD: %s", buf); /* * Are we reading HTTP request (from client) or response (from server)? */ trimr(buf); orig = strdup(buf); len = strlen(buf); tok = strtok_r(buf, " ", &s3); if (tok && (!strncasecmp(buf, "HTTP/", 5) || !strncasecmp(tok, "ICY", 3))) { data->req = 0; data->empty = 0; data->http = strdup(tok); data->msg = NULL; tok = strtok_r(NULL, " ", &s3); if (tok) { ccode = strdup(tok); tok += strlen(ccode); while (tok < buf+len && *tok++ == ' '); if (strlen(tok)) data->msg = strdup(tok); } if (!data->msg) data->msg = strdup(""); if (!ccode || strlen(ccode) != 3 || (data->code = atoi(ccode)) == 0) { i = -2; goto bailout; } } else if (strstr(orig, " HTTP/") && tok) { data->req = 1; data->empty = 0; data->method = NULL; data->url = NULL; data->rel_url = NULL; data->http = NULL; data->hostname = NULL; data->method = strdup(tok); tok = strtok_r(NULL, " ", &s3); if (tok) data->url = strdup(tok); tok = strtok_r(NULL, " ", &s3); if (tok) data->http = strdup(tok); if (!data->url || !data->http) { i = -3; goto bailout; } if ((tok = strstr(data->url, "://"))) { tok += 3; } else { tok = data->url; } s3 = strchr(tok, '/'); if (s3) { host = substr(tok, 0, s3-tok); data->rel_url = strdup(s3); } else { host = substr(tok, 0, strlen(tok)); data->rel_url = strdup("/"); } } else { if (debug) printf("headers_recv: Unknown header (%s).\n", orig); i = -4; goto bailout; } /* * Read in all headers, do not touch any possible HTTP body */ do { i = so_recvln(fd, &buf, &bsize); trimr(buf); if (i > 0 && is_http_header(buf)) { data->headers = hlist_add(data->headers, get_http_header_name(buf), get_http_header_value(buf), HLIST_NOALLOC, HLIST_NOALLOC); } } while (strlen(buf) != 0 && i > 0); if (data->req) { /* * Fix requests, make sure the Host: header is present */ if (host && strlen(host)) { data->hostname = strdup(host); if (!hlist_get(data->headers, "Host")) data->headers = hlist_add(data->headers, "Host", host, HLIST_ALLOC, HLIST_ALLOC); } else { if (debug) printf("headers_recv: no host name (%s)\n", orig); i = -6; goto bailout; } /* * Remove port number from internal host name variable */ if (data->hostname && (tok = strchr(data->hostname, ':'))) { *tok = 0; data->port = atoi(tok+1); } else if (data->url) { if (!strncasecmp(data->url, "https", 5)) data->port = 443; else data->port = 80; } if (!strlen(data->hostname) || !data->port) { i = -5; goto bailout; } } bailout: if (orig) free(orig); if (ccode) free(ccode); if (host) free(host); free(buf); if (i <= 0) { if (debug) printf("headers_recv: fd %d error %d\n", fd, i); return 0; } return 1; } /* * Send HTTP request/response to the given socket based on what's in "data". * Returns: 1 if OK, 0 in case of socket error */ int headers_send(int fd, rr_data_t data) { hlist_t t; char *buf; int i, len; /* * First compute required buffer size (avoid realloc, etc) */ if (data->req) len = 20 + strlen(data->method) + strlen(data->url) + strlen(data->http); else len = 20 + strlen(data->http) + strlen(data->msg); t = data->headers; while (t) { len += 20 + strlen(t->key) + strlen(t->value); t = t->next; } /* * We know how much memory we need now... */ buf = new(len); /* * Prepare the first request/response line */ len = 0; if (data->req) len = sprintf(buf, "%s %s %s\r\n", data->method, data->url, data->http); else if (!data->skip_http) len = sprintf(buf, "%s %03d %s\r\n", data->http, data->code, data->msg); /* * Now add all headers. */ t = data->headers; while (t) { len += sprintf(buf+len, "%s: %s\r\n", t->key, t->value); t = t->next; } /* * Terminate headers */ strcat(buf, "\r\n"); /* * Flush it all down the toilet */ if (!so_closed(fd)) i = write(fd, buf, len+2); else i = -999; free(buf); if (i <= 0 || i != len+2) { if (debug) printf("headers_send: fd %d warning %d (connection closed)\n", fd, i); return 0; } return 1; } /* * Forward "size" of data from "src" to "dst". If size == -1 then keep * forwarding until src reaches EOF. * If dst == -1, data is discarded. */ int data_send(int dst, int src, length_t len) { char *buf; int i, block; int c = 0; int j = 1; if (!len) return 1; buf = new(BLOCK); do { block = (len == -1 || len-c > BLOCK ? BLOCK : len-c); i = read(src, buf, block); if (i > 0) c += i; if (dst >= 0 && debug) printf("data_send: read %d of %d / %d of %lld (errno = %s)\n", i, block, c, len, i < 0 ? strerror(errno) : "ok"); if (dst >= 0 && so_closed(dst)) { i = -999; break; } if (dst >= 0 && i > 0) { j = write(dst, buf, i); if (debug) printf("data_send: wrote %d of %d\n", j, i); } } while (i > 0 && j > 0 && (len == -1 || c < len)); free(buf); if (i <= 0 || j <= 0) { if (i == 0 && j > 0 && (len == -1 || c == len)) return 1; if (debug) printf("data_send: fds %d:%d warning %d (connection closed)\n", dst, src, i); return 0; } return 1; } /* * Forward chunked HTTP body from "src" descriptor to "dst". * If dst == -1, data is discarded. */ int chunked_data_send(int dst, int src) { char *buf; int bsize; int i, w, csize; char *err = NULL; bsize = BUFSIZE; buf = new(bsize); /* Take care of all chunks */ do { i = so_recvln(src, &buf, &bsize); if (i <= 0) { if (debug) printf("chunked_data_send: aborting, read error\n"); free(buf); return 0; } csize = strtol(buf, &err, 16); if (!isspace(*err) && *err != ';') { if (debug) printf("chunked_data_send: aborting, chunk size format error\n"); free(buf); return 0; } if (dst >= 0) i = write(dst, buf, strlen(buf)); if (csize) if (!data_send(dst, src, csize+2)) { if (debug) printf("chunked_data_send: aborting, data_send failed\n"); free(buf); return 0; } } while (csize != 0); /* Take care of possible trailer */ do { i = so_recvln(src, &buf, &bsize); if (dst >= 0 && i > 0) w = write(dst, buf, strlen(buf)); } while (i > 0 && buf[0] != '\r' && buf[0] != '\n'); free(buf); return 1; } /* * Full-duplex forwarding between proxy and client descriptors. * Used for bidirectional HTTP CONNECT connection. */ int tunnel(int cd, int sd) { fd_set set; int from, to, ret, sel; char *buf; buf = new(BUFSIZE); if (debug) printf("tunnel: select cli: %d, srv: %d\n", cd, sd); do { FD_ZERO(&set); FD_SET(cd, &set); FD_SET(sd, &set); sel = select(FD_SETSIZE, &set, NULL, NULL, NULL); if (sel > 0) { if (FD_ISSET(cd, &set)) { from = cd; to = sd; } else { from = sd; to = cd; } ret = read(from, buf, BUFSIZE); if (ret > 0) { ret = write(to, buf, ret); } else { free(buf); return (ret == 0); } } else if (sel < 0) { free(buf); return 0; } } while (1); free(buf); return 1; } /* * Return 0 if no body, -1 if body until EOF, number if size known * One of request/response can be NULL */ length_t http_has_body(rr_data_t request, rr_data_t response) { rr_data_t current; length_t length; int nobody; char *tmp; /* * Are we checking a complete req+res conversation or just the * request body? */ current = (!response || response->empty ? request : response); /* * HTTP body length decisions. There MUST NOT be any body from * server if the request was HEAD or reply is 1xx, 204 or 304. * No body can be in GET request if direction is from client. */ if (current == response) { nobody = (HEAD(request) || (response->code >= 100 && response->code < 200) || response->code == 204 || response->code == 304); } else { nobody = GET(request) || HEAD(request); } /* * Otherwise consult Content-Length. If present, we forward exaclty * that many bytes. * * If not present, but there is Transfer-Encoding or Content-Type * (or a request to close connection, that is, end of data is signaled * by remote close), we will forward until EOF. * * No C-L, no T-E, no C-T == no body. */ tmp = hlist_get(current->headers, "Content-Length"); if (!nobody && tmp == NULL && (hlist_in(current->headers, "Content-Type") || hlist_in(current->headers, "Transfer-Encoding") || hlist_subcmp(current->headers, "Connection", "close"))) { // || (response->code == 200) if (hlist_in(current->headers, "Transfer-Encoding") && hlist_subcmp(current->headers, "Transfer-Encoding", "chunked")) length = 1; else length = -1; } else length = (tmp == NULL || nobody ? 0 : atoll(tmp)); if (current == request && length == -1) length = 0; return length; } /* * Send a HTTP body (if any) between descriptors readfd and writefd */ int http_body_send(int writefd, int readfd, rr_data_t request, rr_data_t response) { length_t bodylen; int rc = 1; rr_data_t current; /* * Are we checking a complete req+res conversation or just the * request body? */ current = (response->empty ? request : response); /* * Ok, so do we expect any body? */ bodylen = http_has_body(request, response); if (bodylen) { /* * Check for supported T-E. */ if (hlist_subcmp(current->headers, "Transfer-Encoding", "chunked")) { if (debug) printf("Chunked body included.\n"); rc = chunked_data_send(writefd, readfd); if (debug) printf(rc ? "Chunked body sent.\n" : "Could not chunk send whole body\n"); } else { if (debug) printf("Body included. Length: %lld\n", bodylen); rc = data_send(writefd, readfd, bodylen); if (debug) printf(rc ? "Body sent.\n" : "Could not send whole body\n"); } } else if (debug) printf("No body.\n"); return rc; } /* * Connection cleanup - C-L or chunked body * Return 0 if connection closed or EOF, 1 if OK to continue */ int http_body_drop(int fd, rr_data_t response) { length_t bodylen; int rc = 1; bodylen = http_has_body(NULL, response); if (bodylen) { if (hlist_subcmp(response->headers, "Transfer-Encoding", "chunked")) { if (debug) printf("Discarding chunked body.\n"); rc = chunked_data_send(-1, fd); } else { if (debug) printf("Discarding %lld bytes.\n", bodylen); rc = data_send(-1, fd, bodylen); } } return rc; } /* * Parse headers for BASIC auth credentials * * Return 1 = creds parsed OK, 0 = no creds, -1 = invalid creds */ int http_parse_basic(hlist_t headers, const char *header, struct auth_s *tcreds) { char *tmp = NULL, *pos = NULL, *buf = NULL, *dom = NULL; int i; if (!hlist_subcmp(headers, header, "basic")) return 0; tmp = hlist_get(headers, header); buf = new(strlen(tmp) + 1); i = 5; while (i < strlen(tmp) && tmp[++i] == ' '); from_base64(buf, tmp+i); pos = strchr(buf, ':'); if (pos == NULL) { memset(buf, 0, strlen(buf)); /* clean password memory */ free(buf); return -1; } else { *pos = 0; dom = strchr(buf, '\\'); if (dom == NULL) { auth_strcpy(tcreds, user, buf); } else { *dom = 0; auth_strcpy(tcreds, domain, buf); auth_strcpy(tcreds, user, dom+1); } if (tcreds->hashntlm2) { tmp = ntlm2_hash_password(tcreds->user, tcreds->domain, pos+1); auth_memcpy(tcreds, passntlm2, tmp, 16); free(tmp); } if (tcreds->hashnt) { tmp = ntlm_hash_nt_password(pos+1); auth_memcpy(tcreds, passnt, tmp, 21); free(tmp); } if (tcreds->hashlm) { tmp = ntlm_hash_lm_password(pos+1); auth_memcpy(tcreds, passlm, tmp, 21); free(tmp); } memset(buf, 0, strlen(buf)); free(buf); } return 1; } cntlm-0.92.3/Makefile0000664000175000017500000001136511724144253013335 0ustar daviddavid# # You can tweak these three variables to make things install where you # like, but do not touch more unless you know what you are doing. ;) # DESTDIR= SYSCONFDIR=$(DESTDIR)/etc BINDIR=$(DESTDIR)/usr/sbin MANDIR=$(DESTDIR)/usr/share/man # # Careful now... # __BSD_VISIBLE is for FreeBSD AF_* constants # _ALL_SOURCE is for AIX 5.3 LOG_PERROR constant # NAME=cntlm CC=gcc VER=`cat VERSION` CFLAGS+=$(FLAGS) -std=c99 -Wall -Wno-unused-but-set-variable -pedantic -O3 -D__BSD_VISIBLE -D_ALL_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112 -D_ISOC99_SOURCE -D_REENTRANT -D_BSD_SOURCE -DVERSION=\"`cat VERSION`\" OS=$(shell uname -s) OSLDFLAGS=$(shell [ $(OS) = "SunOS" ] && echo "-lrt -lsocket -lnsl") LDFLAGS:=-lpthread $(OSLDFLAGS) ifeq ($(findstring CYGWIN,$(OS)),) OBJS=utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o main.o else OBJS=utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o main.o win/resources.o endif $(NAME): configure-stamp $(OBJS) @echo "Linking $@" @$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) main.o: main.c @echo "Compiling $<" @if [ -z "$(SYSCONFDIR)" ]; then \ $(CC) $(CFLAGS) -c main.c -o $@; \ else \ $(CC) $(CFLAGS) -DSYSCONFDIR=\"$(SYSCONFDIR)\" -c main.c -o $@; \ fi .c.o: @echo "Compiling $<" @$(CC) $(CFLAGS) -c -o $@ $< install: $(NAME) # Special handling for install(1) if [ "`uname -s`" = "AIX" ]; then \ install -M 755 -S -f $(BINDIR) $(NAME); \ install -M 644 -f $(MANDIR)/man1 doc/$(NAME).1; \ install -M 600 -c $(SYSCONFDIR) doc/$(NAME).conf; \ elif [ "`uname -s`" = "Darwin" ]; then \ install -d -m 755 -s $(NAME) $(BINDIR)/$(NAME); \ install -d -m 644 doc/$(NAME).1 $(MANDIR)/man1/$(NAME).1; \ [ -f $(SYSCONFDIR)/$(NAME).conf -o -z "$(SYSCONFDIR)" ] \ || install -d -m 600 doc/$(NAME).conf $(SYSCONFDIR)/$(NAME).conf; \ else \ install -D -m 755 -s $(NAME) $(BINDIR)/$(NAME); \ install -D -m 644 doc/$(NAME).1 $(MANDIR)/man1/$(NAME).1; \ [ -f $(SYSCONFDIR)/$(NAME).conf -o -z "$(SYSCONFDIR)" ] \ || install -D -m 600 doc/$(NAME).conf $(SYSCONFDIR)/$(NAME).conf; \ fi @echo; echo "Cntlm will look for configuration in $(SYSCONFDIR)/$(NAME).conf" tgz: mkdir -p tmp rm -rf tmp/$(NAME)-$(VER) svn export . tmp/$(NAME)-$(VER) tar zcvf $(NAME)-$(VER).tar.gz -C tmp/ $(NAME)-$(VER) rm -rf tmp/$(NAME)-$(VER) rmdir tmp 2>/dev/null || true tbz2: mkdir -p tmp rm -rf tmp/$(NAME)-$(VER) svn export . tmp/$(NAME)-$(VER) tar jcvf $(NAME)-$(VER).tar.bz2 -C tmp/ $(NAME)-$(VER) rm -rf tmp/$(NAME)-$(VER) rmdir tmp 2>/dev/null || true deb: builddeb builddeb: sed -i "s/^\(cntlm *\)([^)]*)/\1($(VER))/g" debian/changelog if [ `id -u` = 0 ]; then \ debian/rules binary; \ debian/rules clean; \ else \ fakeroot debian/rules binary; \ fakeroot debian/rules clean; \ fi mv ../cntlm_$(VER)*.deb . rpm: buildrpm buildrpm: sed -i "s/^\(Version:[\t ]*\)\(.*\)/\1$(VER)/g" rpm/cntlm.spec if [ `id -u` = 0 ]; then \ rpm/rules binary; \ rpm/rules clean; \ else \ fakeroot rpm/rules binary; \ fakeroot rpm/rules clean; \ fi win: buildwin buildwin: @echo @echo "* This build target must be run from a Cywgin shell on Windows *" @echo "* and you also need InnoSetup installed *" @echo rm -f win/cntlm_manual.pdf groff -t -e -mandoc -Tps doc/cntlm.1 | ps2pdf - win/cntlm_manual.pdf cat doc/cntlm.conf | unix2dos > win/cntlm.ini cat COPYRIGHT LICENSE | unix2dos > win/license.txt sed "s/\$$VERSION/$(VER)/g" win/setup.iss.in > win/setup.iss cp /bin/cygwin1.dll /bin/cyggcc_s-1.dll /bin/cygrunsrv.exe win/ cp cntlm.exe win/ strip win/cntlm.exe ln -s win $(NAME)-$(VER) zip -9 $(NAME)-$(VER).zip $(NAME)-$(VER)/cntlm.exe $(NAME)-$(VER)/cyggcc_s-1.dll $(NAME)-$(VER)/cygwin1.dll $(NAME)-$(VER)/cygrunsrv.exe $(NAME)-$(VER)/cntlm.ini $(NAME)-$(VER)/README.txt $(NAME)-$(VER)/license.txt rm -f $(NAME)-$(VER) @echo @echo Now open folder "win", right-click "setup.iss", then "Compile". @echo InnoSetup will generate a new installer cntlm-X.XX-setup.exe @echo win/resources.o: win/resources.rc @echo Adding EXE resources @windres $^ -o $@ uninstall: rm -f $(BINDIR)/$(NAME) $(MANDIR)/man1/$(NAME).1 2>/dev/null || true clean: @rm -f *.o cntlm cntlm.exe configure-stamp build-stamp config/config.h 2>/dev/null @rm -f win/*.exe win/*.dll win/*.iss win/*.pdf win/cntlm.ini win/license.txt win/resouces.o 2>/dev/null @rm -f config/endian config/gethostname config/strdup config/socklen_t config/*.exe @if [ -h Makefile ]; then rm -f Makefile; mv Makefile.gcc Makefile; fi distclean: clean if [ `id -u` = 0 ]; then \ debian/rules clean; \ rpm/rules clean; \ else \ fakeroot debian/rules clean; \ fakeroot rpm/rules clean; \ fi @rm -f *.exe *.deb *.rpm *.tgz *.tar.gz *.tar.bz2 tags ctags pid 2>/dev/null cntlm-0.92.3/direct.h0000664000175000017500000000174711363563777013342 0ustar daviddavid/* * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _DIRECT_H #define _DIRECT_H #include "utils.h" extern int host_connect(const char *hostname, int port); extern rr_data_t direct_request(void *cdata, rr_data_t request); extern void direct_tunnel(void *thread_data); #endif /* _DIRECT_H */ cntlm-0.92.3/forward.c0000664000175000017500000006525411724010407013504 0ustar daviddavid/* * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "globals.h" #include "auth.h" #include "http.h" #include "socket.h" #include "ntlm.h" #include "forward.h" #include "scanner.h" #include "pages.h" int parent_curr = 0; pthread_mutex_t parent_mtx = PTHREAD_MUTEX_INITIALIZER; /* * Connect to the selected proxy. If the request fails, pick next proxy * in the line. Each request scans the whole list until all items are tried * or a working proxy is found, in which case it is selected and used by * all threads until it stops working. Then the search starts again. * * Writes required credentials into passed auth_s structure */ int proxy_connect(struct auth_s *credentials) { proxy_t *aux; int i, prev; plist_t list, tmp; int loop = 0; prev = parent_curr; pthread_mutex_lock(&parent_mtx); if (parent_curr == 0) { aux = (proxy_t *)plist_get(parent_list, ++parent_curr); syslog(LOG_INFO, "Using proxy %s:%d\n", aux->hostname, aux->port); } pthread_mutex_unlock(&parent_mtx); do { pthread_mutex_lock(&parent_mtx); aux = (proxy_t *)plist_get(parent_list, parent_curr); pthread_mutex_unlock(&parent_mtx); if (aux->resolved == 0) { if (debug) syslog(LOG_INFO, "Resolving proxy %s...\n", aux->hostname); if (so_resolv(&aux->host, aux->hostname)) { aux->resolved = 1; } else { syslog(LOG_ERR, "Cannot resolve proxy %s\n", aux->hostname); } } i = 0; if (aux->resolved != 0) i = so_connect(aux->host, aux->port); /* * Resolve or connect failed? */ if (i <= 0) { pthread_mutex_lock(&parent_mtx); if (parent_curr >= parent_count) parent_curr = 0; aux = (proxy_t *)plist_get(parent_list, ++parent_curr); pthread_mutex_unlock(&parent_mtx); syslog(LOG_ERR, "Proxy connect failed, will try %s:%d\n", aux->hostname, aux->port); } } while (i <= 0 && ++loop < parent_count); if (i <= 0 && loop >= parent_count) syslog(LOG_ERR, "No proxy on the list works. You lose.\n"); /* * We have to invalidate the cached connections if we moved to a different proxy */ if (prev != parent_curr) { pthread_mutex_lock(&connection_mtx); list = connection_list; while (list) { tmp = list->next; close(list->key); list = tmp; } plist_free(connection_list); pthread_mutex_unlock(&connection_mtx); } if (i > 0 && credentials != NULL) copy_auth(credentials, g_creds, /* fullcopy */ !ntlmbasic); return i; } /* * Send request, read reply, if it contains NTLM challenge, generate final * NTLM auth message and insert it into the original client header, * which is then processed by caller himself. * * If response is present, we fill in proxy's reply. Caller can tell * if auth was required or not from response->code. If not, caller has * a full reply to forward to client. * * Return 0 in case of network error, 1 when proxy replies * * Caller must init & free "request" and "response" (if supplied) * */ int proxy_authenticate(int *sd, rr_data_t request, rr_data_t response, struct auth_s *credentials) { char *tmp, *buf, *challenge; rr_data_t auth; int len; int pretend407 = 0; int rc = 0; buf = new(BUFSIZE); strcpy(buf, "NTLM "); len = ntlm_request(&tmp, credentials); if (len) { to_base64(MEM(buf, uint8_t, 5), MEM(tmp, uint8_t, 0), len, BUFSIZE-5); free(tmp); } auth = dup_rr_data(request); auth->headers = hlist_mod(auth->headers, "Proxy-Authorization", buf, 1); if (HEAD(request) || http_has_body(request, response) != 0) { /* * There's a body - make this request just a probe. Do not send any body. If no auth * is required, we let our caller send the reply directly to the client to avoid * another duplicate request later (which traditionally finishes the 2nd part of * NTLM handshake). Without auth, there's no need for the final request. * * However, if client has a body, we make this request without it and let caller do * the second request in full. If we did it here, we'd have to cache the request * body in memory (even chunked) and carry it around. Not practical. * * When caller sees 407, he makes the second request. That's why we pretend a 407 * in this situation. Without it, caller wouldn't make it, sending the client a * reply to our PROBE, not the real request. * * The same for HEAD requests - at least one ISA doesn't allow making auth * request using HEAD!! */ if (debug) printf("Will send just a probe request.\n"); pretend407 = 1; } /* * For broken ISA's that don't accept HEAD in auth request */ if (HEAD(request)) { free(auth->method); auth->method = strdup("GET"); } auth->headers = hlist_mod(auth->headers, "Content-Length", "0", 1); auth->headers = hlist_del(auth->headers, "Transfer-Encoding"); if (debug) { printf("\nSending PROXY auth request...\n"); hlist_dump(auth->headers); } if (!headers_send(*sd, auth)) { goto bailout; } if (debug) printf("\nReading PROXY auth response...\n"); /* * Return response if requested. "auth" is used to get it, * so make it point to the caller's structure. */ if (response) { free_rr_data(auth); auth = response; } reset_rr_data(auth); if (!headers_recv(*sd, auth)) { goto bailout; } if (debug) hlist_dump(auth->headers); rc = 1; /* * Auth required? */ if (auth->code == 407) { if (!http_body_drop(*sd, auth)) { // FIXME: if below fails, we should forward what we drop here... rc = 0; goto bailout; } tmp = hlist_get(auth->headers, "Proxy-Authenticate"); if (tmp) { challenge = new(strlen(tmp) + 5 + 1); len = from_base64(challenge, tmp + 5); if (len > NTLM_CHALLENGE_MIN) { len = ntlm_response(&tmp, challenge, len, credentials); if (len > 0) { strcpy(buf, "NTLM "); to_base64(MEM(buf, uint8_t, 5), MEM(tmp, uint8_t, 0), len, BUFSIZE-5); request->headers = hlist_mod(request->headers, "Proxy-Authorization", buf, 1); free(tmp); } else { syslog(LOG_ERR, "No target info block. Cannot do NTLMv2!\n"); free(challenge); goto bailout; } } else { syslog(LOG_ERR, "Proxy returning invalid challenge!\n"); free(challenge); goto bailout; } free(challenge); } else { syslog(LOG_WARNING, "No Proxy-Authenticate, NTLM not supported?\n"); } } else if (pretend407) { if (debug) printf("Client %s - forcing second request.\n", HEAD(request) ? "sent HEAD" : "has a body"); if (response) response->code = 407; // See explanation above if (!http_body_drop(*sd, auth)) { rc = 0; goto bailout; } } /* * Did proxy closed connection? It's our fault, reconnect for the caller. */ if (so_closed(*sd)) { if (debug) printf("Proxy closed on us, reconnect.\n"); close(*sd); *sd = proxy_connect(credentials); if (*sd < 0) { rc = 0; goto bailout; } } bailout: if (!response) free_rr_data(auth); free(buf); return rc; } /* * Forwarding thread. Connect to the proxy, process auth then request. * * First read request, then call proxy_authenticate() which will send * the request. If proxy returns 407, it will compute NTLM reply and * return authenticated request to us. If proxy returns full response * (no auth needed), it returns the full reply. Then we just forward * the reply to client OR make the request again with properly auth'd * headers provided by proxy_authenticate(). * * We loop while we see Connection: keep-alive, thus making sure clients * can have uninterrupted conversations with a web server. Proxy-Connection * is not our concern, it's handled in the caller, proxy_thread(). If it's * present, however, we cache the auth'd proxy connection for reuse. * * Some proxies return Connection: keep-alive even when not requested and * would make us loop indefinitely. Because of that, we remember which server * we're talking to and if that changes, we return the request to be processed * by our caller. * * Caller decides which URL's to forward and which to process directly, that's * also why we return the request if the server name changes. * * We return NULL when we're finished or a pointer to another request. * Returned request means server name has changed and needs to be checked * agains NoProxy exceptions. * * thread_data is NOT freed * request is NOT freed */ rr_data_t forward_request(void *thread_data, rr_data_t request) { int i, w, loop, plugin, retry = 0; int *rsocket[2], *wsocket[2]; rr_data_t data[2], rc = NULL; hlist_t tl; char *tmp; struct auth_s *tcreds = NULL; /* Per-thread credentials */ char *hostname = NULL; int proxy_alive; int conn_alive; int authok; int noauth; int was_cached; int sd; int cd = ((struct thread_arg_s *)thread_data)->fd; struct sockaddr_in caddr = ((struct thread_arg_s *)thread_data)->addr; beginning: sd = was_cached = noauth = authok = conn_alive = proxy_alive = 0; rsocket[0] = wsocket[1] = &cd; rsocket[1] = wsocket[0] = &sd; if (debug) { printf("Thread processing%s...\n", retry ? " (retry)" : ""); pthread_mutex_lock(&connection_mtx); plist_dump(connection_list); pthread_mutex_unlock(&connection_mtx); } /* * NTLM credentials for purposes of this thread (tcreds) are given to * us by proxy_connect() or retrieved from connection cache. * * Ultimately, the source for creds is always proxy_connect(), but when * we cache a connection, we store creds associated with it in the * cache as well, in case we'll need them. */ pthread_mutex_lock(&connection_mtx); i = plist_pop(&connection_list, (void **)&tcreds); pthread_mutex_unlock(&connection_mtx); if (i) { if (debug) printf("Found autenticated connection %d!\n", i); sd = i; authok = 1; was_cached = 1; } else { tcreds = new_auth(); sd = proxy_connect(tcreds); if (sd <= 0) { tmp = gen_502_page(request->http, "Parent proxy unreacheable"); w = write(cd, tmp, strlen(tmp)); free(tmp); rc = (void *)-1; goto bailout; } } /* * Each thread only serves req's for one hostname. If hostname changes, * we return request to our caller for a new direct/forward decision. */ if (!hostname && request->hostname) { hostname = strdup(request->hostname); } do { /* * data[0] is for the first loop pass * - first do {} loop iteration uses request passed from caller, * in subsequent iterations we read the request headers from the client * - if not already done, we try to authenticate the connection * - we send the request headers to the proxy with HTTP body, if present * * data[1] is for the second pass * - read proxy response * - forward it to the client with HTTP body, if present * * There two goto's: * - beginning: jump here to retry request (when cached connection timed out * or we thought proxy was notauth, but got 407) * - shortcut: jump here from 1st iter. of inner loop, when we detect * that auth isn't required by proxy. We do loop++, make the jump and * the reply to our auth attempt (containing valid response) is sent to * client directly without us making a request a second time. */ if (request) { if (retry) data[0] = request; // Got from inside the loop = retry (must free ourselves) else data[0] = dup_rr_data(request); // Got from caller (make a dup, caller will free) request = NULL; // Next time, just alloc empty structure } else { data[0] = new_rr_data(); } data[1] = new_rr_data(); retry = 0; proxy_alive = 0; conn_alive = 0; for (loop = 0; loop < 2; ++loop) { if (data[loop]->empty) { // Isn't this the first loop with request supplied by caller? if (debug) { printf("\n******* Round %d C: %d, S: %d (authok=%d, noauth=%d) *******\n", loop+1, cd, sd, authok, noauth); printf("Reading headers (%d)...\n", *rsocket[loop]); } if (!headers_recv(*rsocket[loop], data[loop])) { free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; /* error page */ goto bailout; } } /* * Check whether this new request still talks to the same server as previous. * If no, return request to caller, he must decide on forward or direct * approach. * * If we're here, previous request loop must have been proxy keep-alive * (we're looping only if proxy_alive) or this is the first loop since * we were called. If former, set proxy_alive=1 to cache the connection. */ if (loop == 0 && hostname && data[0]->hostname && strcasecmp(hostname, data[0]->hostname)) { if (debug) printf("\n******* F RETURN: %s *******\n", data[0]->url); if (authok) proxy_alive = 1; rc = dup_rr_data(data[0]); free_rr_data(data[0]); free_rr_data(data[1]); goto bailout; } if (debug) hlist_dump(data[loop]->headers); if (loop == 0 && data[0]->req) syslog(LOG_DEBUG, "%s %s %s", inet_ntoa(caddr.sin_addr), data[0]->method, data[0]->url); shortcut: /* * Modify request headers. * * Try to request keep-alive for every connection. We keep them in a pool * for future reuse. */ if (loop == 0 && data[0]->req) { /* * NTLM-to-Basic */ if (http_parse_basic(data[loop]->headers, "Proxy-Authorization", tcreds) > 0) { if (debug) printf("NTLM-to-basic: Credentials parsed: %s\\%s at %s\n", tcreds->domain, tcreds->user, tcreds->workstation); } else if (ntlmbasic) { if (debug) printf("NTLM-to-basic: Returning client auth request.\n"); tmp = gen_407_page(data[loop]->http); w = write(cd, tmp, strlen(tmp)); free(tmp); free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } /* * Header replacement implementation */ tl = header_list; while (tl) { data[0]->headers = hlist_mod(data[0]->headers, tl->key, tl->value, 0); tl = tl->next; } /* * Also remove runaway P-A from the client (e.g. Basic from N-t-B), which might * cause some ISAs to deny us, even if the connection is already auth'd. */ data[0]->headers = hlist_mod(data[0]->headers, "Proxy-Connection", "keep-alive", 1); /* * Remove all Proxy-Authorization headers from client */ while (hlist_get(data[loop]->headers, "Proxy-Authorization")) { data[loop]->headers = hlist_del(data[loop]->headers, "Proxy-Authorization"); } } /* * Got request from client and connection is not yet authenticated? * This can happen only with non-cached connections. */ if (loop == 0 && data[0]->req && !authok && !noauth) { if (!proxy_authenticate(wsocket[0], data[0], data[1], tcreds)) { if (debug) printf("Proxy auth connection error.\n"); free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; /* error page */ goto bailout; } /* * !!! data[1] is now filled by proxy_authenticate() !!! * !!! with proxy's reply to our first (auth) req. !!! * !!! that's why we reset data[1] below !!! * * Reply to auth request wasn't 407? Then auth is not required, * let's jump into the next loop and forward it to client * Also just forward if proxy doesn't reply with keep-alive, * because without it, NTLM auth wouldn't work anyway. * * Let's decide proxy doesn't want any auth if it returns a * non-error reply. Next rounds will be faster. */ if (data[1]->code != 407) { // || !hlist_subcmp(data[1]->headers, "Proxy-Connection", "keep-alive")) { if (debug) printf("Proxy auth not requested - just forwarding.\n"); if (data[1]->code < 400) noauth = 1; loop = 1; goto shortcut; } /* * If we're continuing normally, we have to free possible * auth response from proxy_authenticate() in data[1] */ reset_rr_data(data[1]); } /* * Is final reply from proxy still 407 denied? If this is a chached * connection or we thougth proxy was noauth (so we didn't auth), make a new * connect and try to auth. */ if (loop == 1 && data[1]->code == 407 && (was_cached || noauth)) { if (debug) printf("\nFinal reply is 407 - retrying (cached=%d, noauth=%d).\n", was_cached, noauth); if (tcreds) free(tcreds); retry = 1; request = data[0]; free_rr_data(data[1]); close(sd); goto beginning; } /* * Was the request first and did we authenticate with proxy? * Remember not to authenticate this connection any more. */ if (loop == 1 && !noauth && data[1]->code != 407) authok = 1; /* * This is to make the ISA AV scanner bullshit transparent. If the page * returned is scan-progress-html-fuck instead of requested file/data, parse * it, wait for completion, make a new request to ISA for the real data and * substitute the result for the original response html-fuck response. */ plugin = PLUG_ALL; if (loop == 1 && scanner_plugin) { plugin = scanner_hook(data[0], data[1], tcreds, *wsocket[loop], rsocket[loop], scanner_plugin_maxsize); } /* * Check if we should loop for another request. Required for keep-alive * connections, client might really need a non-interrupted conversation. * * We check only server reply for keep-alive, because client may want it, * but it's not gonna happen unless server agrees. */ if (loop == 1) { conn_alive = hlist_subcmp(data[1]->headers, "Connection", "keep-alive"); if (!conn_alive) data[1]->headers = hlist_mod(data[1]->headers, "Connection", "close", 1); /* * Remove all Proxy-Authenticate headers from proxy */ while (hlist_get(data[loop]->headers, "Proxy-Authenticate")) { data[loop]->headers = hlist_del(data[loop]->headers, "Proxy-Authenticate"); } /* * Are we returning 407 to the client? Substitute his request * by our BASIC translation request. */ if (data[1]->code == 407) { data[1]->headers = hlist_mod(data[1]->headers, "Proxy-Authenticate", "Basic realm=\"Cntlm for parent\"", 1); } } if (plugin & PLUG_SENDHEAD) { if (debug) { printf("Sending headers (%d)...\n", *wsocket[loop]); if (loop == 0) hlist_dump(data[loop]->headers); } /* * Forward client's headers to the proxy and vice versa; proxy_authenticate() * might have by now prepared 1st and 2nd auth steps and filled our headers with * the 3rd, final, NTLM message. */ if (!headers_send(*wsocket[loop], data[loop])) { free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; /* error page */ goto bailout; } } /* * Was the request CONNECT and proxy agreed? */ if (loop == 1 && CONNECT(data[0]) && data[1]->code == 200) { if (debug) printf("Ok CONNECT response. Tunneling...\n"); tunnel(cd, sd); free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } if (plugin & PLUG_SENDDATA) { if (!http_body_send(*wsocket[loop], *rsocket[loop], data[0], data[1])) { free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } } /* * Proxy-Connection: keep-alive is taken care of in our caller as I said, * but we do return when we see proxy is closing. Next headers_recv() would * fail and we'd exit anyway. * * This way, we also tell our caller that proxy keep-alive is impossible. */ if (loop == 1) { proxy_alive = hlist_subcmp(data[loop]->headers, "Proxy-Connection", "keep-alive"); if (!proxy_alive) { if (debug) printf("PROXY CLOSING CONNECTION\n"); rc = (void *)-1; } } } free_rr_data(data[0]); free_rr_data(data[1]); /* * Checking conn_alive && proxy_alive is sufficient, * so_closed() just eliminates loops that we know would fail. */ } while (conn_alive && proxy_alive && !so_closed(sd) && !so_closed(cd) && !serialize); bailout: if (hostname) free(hostname); if (debug) { printf("forward_request: palive=%d, authok=%d, ntlm=%d, closed=%d\n", proxy_alive, authok, ntlmbasic, so_closed(sd)); printf("\nThread finished.\n"); } if (proxy_alive && authok && !ntlmbasic && !so_closed(sd)) { if (debug) printf("Storing the connection for reuse (%d:%d).\n", cd, sd); pthread_mutex_lock(&connection_mtx); connection_list = plist_add(connection_list, sd, (void *)tcreds); pthread_mutex_unlock(&connection_mtx); } else { free(tcreds); close(sd); } return rc; } /* * Auth connection "sd" and try to return negotiated CONNECT * connection to a remote host:port (thost). * * Return 1 for success, 0 failure. */ int prepare_http_connect(int sd, struct auth_s *credentials, const char *thost) { rr_data_t data1, data2; int rc = 0; hlist_t tl; if (!sd || !thost || !strlen(thost)) return 0; data1 = new_rr_data(); data2 = new_rr_data(); data1->req = 1; data1->method = strdup("CONNECT"); data1->url = strdup(thost); data1->http = strdup("HTTP/1.1"); data1->headers = hlist_mod(data1->headers, "Proxy-Connection", "keep-alive", 1); /* * Header replacement */ tl = header_list; while (tl) { data1->headers = hlist_mod(data1->headers, tl->key, tl->value, 1); tl = tl->next; } if (debug) printf("Starting authentication...\n"); if (proxy_authenticate(&sd, data1, data2, credentials)) { /* * Let's try final auth step, possibly changing data2->code */ if (data2->code == 407) { if (debug) { printf("Sending real request:\n"); hlist_dump(data1->headers); } if (!headers_send(sd, data1)) { printf("Sending request failed!\n"); goto bailout; } if (debug) printf("\nReading real response:\n"); reset_rr_data(data2); if (!headers_recv(sd, data2)) { if (debug) printf("Reading response failed!\n"); goto bailout; } if (debug) hlist_dump(data2->headers); } if (data2->code == 200) { if (debug) printf("Ok CONNECT response. Tunneling...\n"); rc = 1; } else if (data2->code == 407) { syslog(LOG_ERR, "Authentication for tunnel %s failed!\n", thost); } else { syslog(LOG_ERR, "Request for CONNECT to %s denied!\n", thost); } } else syslog(LOG_ERR, "Tunnel requests failed!\n"); bailout: free_rr_data(data1); free_rr_data(data2); return rc; } void forward_tunnel(void *thread_data) { struct auth_s *tcreds; int sd; int cd = ((struct thread_arg_s *)thread_data)->fd; char *thost = ((struct thread_arg_s *)thread_data)->target; struct sockaddr_in caddr = ((struct thread_arg_s *)thread_data)->addr; tcreds = new_auth(); sd = proxy_connect(tcreds); if (sd <= 0) goto bailout; syslog(LOG_DEBUG, "%s TUNNEL %s", inet_ntoa(caddr.sin_addr), thost); if (debug) printf("Tunneling to %s for client %d...\n", thost, cd); if (prepare_http_connect(sd, tcreds, thost)) tunnel(cd, sd); bailout: close(sd); close(cd); free(tcreds); return; } #define MAGIC_TESTS 4 void magic_auth_detect(const char *url) { int i, nc, c, ign = 0, found = -1; rr_data_t req, res; char *tmp, *pos, *host = NULL; struct auth_s *tcreds; char *authstr[5] = { "NTLMv2", "NTLM2SR", "NT", "NTLM", "LM" }; int prefs[MAGIC_TESTS][5] = { /* NT, LM, NTLMv2, Flags, index to authstr[] */ { 0, 0, 1, 0, 0 }, { 1, 1, 0, 0, 3 }, { 0, 1, 0, 0, 4 }, { 2, 0, 0, 0, 1 } }; tcreds = new_auth(); copy_auth(tcreds, g_creds, /* fullcopy */ 1); if (!tcreds->passnt || !tcreds->passlm || !tcreds->passntlm2) { printf("Cannot detect NTLM dialect - password or all its hashes must be defined, try -I\n"); exit(1); } pos = strstr(url, "://"); if (pos) { tmp = strchr(pos+3, '/'); host = substr(pos+3, 0, tmp ? tmp-pos-3 : 0); } else { fprintf(stderr, "Invalid URL (%s)\n", url); return; } for (i = 0; i < MAGIC_TESTS; ++i) { res = new_rr_data(); req = new_rr_data(); req->req = 1; req->method = strdup("GET"); req->url = strdup(url); req->http = strdup("HTTP/1.1"); req->headers = hlist_add(req->headers, "Proxy-Connection", "keep-alive", HLIST_ALLOC, HLIST_ALLOC); if (host) req->headers = hlist_add(req->headers, "Host", host, HLIST_ALLOC, HLIST_ALLOC); tcreds->hashnt = prefs[i][0]; tcreds->hashlm = prefs[i][1]; tcreds->hashntlm2 = prefs[i][2]; tcreds->flags = prefs[i][3]; printf("Config profile %2d/%d... ", i+1, MAGIC_TESTS); nc = proxy_connect(NULL); if (nc <= 0) { printf("\nConnection to proxy failed, bailing out\n"); free_rr_data(res); free_rr_data(req); close(nc); if (host) free(host); return; } c = proxy_authenticate(&nc, req, res, tcreds); if (c && res->code != 407) { ign++; printf("Auth not required (HTTP code: %d)\n", res->code); free_rr_data(res); free_rr_data(req); close(nc); continue; } reset_rr_data(res); if (!headers_send(nc, req) || !headers_recv(nc, res)) { printf("Connection closed\n"); } else { if (res->code == 407) { if (hlist_subcmp_all(res->headers, "Proxy-Authenticate", "NTLM") || hlist_subcmp_all(res->headers, "Proxy-Authenticate", "BASIC")) { printf("Credentials rejected\n"); } else { printf("Proxy doesn't offer NTLM or BASIC\n"); break; } } else { printf("OK (HTTP code: %d)\n", res->code); if (found < 0) { found = i; free_rr_data(res); free_rr_data(req); close(nc); break; } } } free_rr_data(res); free_rr_data(req); close(nc); } if (found > -1) { printf("----------------------------[ Profile %2d ]------\n", found); printf("Auth %s\n", authstr[prefs[found][4]]); if (prefs[found][3]) printf("Flags 0x%x\n", prefs[found][3]); if (prefs[found][0]) { printf("PassNT %s\n", tmp=printmem(tcreds->passnt, 16, 8)); free(tmp); } if (prefs[found][1]) { printf("PassLM %s\n", tmp=printmem(tcreds->passlm, 16, 8)); free(tmp); } if (prefs[found][2]) { printf("PassNTLMv2 %s\n", tmp=printmem(tcreds->passntlm2, 16, 8)); free(tmp); } printf("------------------------------------------------\n"); } else if (ign == MAGIC_TESTS) { printf("\nYour proxy is open, you don't need another proxy.\n"); } else printf("\nWrong credentials, invalid URL or proxy doesn't support NTLM nor BASIC.\n"); if (host) free(host); } cntlm-0.92.3/forward.h0000664000175000017500000000235011363563777013523 0ustar daviddavid/* * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _FORWARD_H #define _FORWARD_H #include "utils.h" #include "auth.h" extern int proxy_connect(struct auth_s *credentials); extern int proxy_authenticate(int *sd, rr_data_t request, rr_data_t response, struct auth_s *creds); extern int prepare_http_connect(int sd, struct auth_s *credentials, const char *thost); extern rr_data_t forward_request(void *cdata, rr_data_t request); extern void forward_tunnel(void *thread_data); extern void magic_auth_detect(const char *url); #endif /* _FORWARD_H */ cntlm-0.92.3/direct.c0000664000175000017500000002615711664711303013317 0ustar daviddavid/* * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include #include #include #include #include #include #include #include #include extern int h_errno; #include "utils.h" #include "globals.h" #include "auth.h" #include "http.h" #include "socket.h" #include "ntlm.h" #include "direct.h" #include "pages.h" int host_connect(const char *hostname, int port) { struct in_addr addr; errno = 0; if (!so_resolv(&addr, hostname)) { //if (debug) // printf("so_resolv: %s failed (%d: %s)\n", hostname, h_errno, hstrerror(h_errno)); return -1; } return so_connect(addr, port); } int www_authenticate(int sd, rr_data_t request, rr_data_t response, struct auth_s *creds) { char *tmp, *buf, *challenge; rr_data_t auth; int len; int rc = 0; buf = new(BUFSIZE); strcpy(buf, "NTLM "); len = ntlm_request(&tmp, creds); if (len) { to_base64(MEM(buf, uint8_t, 5), MEM(tmp, uint8_t, 0), len, BUFSIZE-5); free(tmp); } auth = dup_rr_data(request); auth->headers = hlist_mod(auth->headers, "Connection", "keep-alive", 1); auth->headers = hlist_mod(auth->headers, "Authorization", buf, 1); auth->headers = hlist_mod(auth->headers, "Content-Length", "0", 1); auth->headers = hlist_del(auth->headers, "Transfer-Encoding"); /* * Drop whatever error page server returned */ if (!http_body_drop(sd, response)) goto bailout; if (debug) { printf("\nSending WWW auth request...\n"); hlist_dump(auth->headers); } if (!headers_send(sd, auth)) goto bailout; if (debug) printf("\nReading WWW auth response...\n"); /* * Get NTLM challenge */ reset_rr_data(auth); if (!headers_recv(sd, auth)) { goto bailout; } if (debug) hlist_dump(auth->headers); /* * Auth required? */ if (auth->code == 401) { if (!http_body_drop(sd, auth)) goto bailout; tmp = hlist_get(auth->headers, "WWW-Authenticate"); if (tmp && strlen(tmp) > 6 + 8) { challenge = new(strlen(tmp) + 5 + 1); len = from_base64(challenge, tmp + 5); if (len > NTLM_CHALLENGE_MIN) { len = ntlm_response(&tmp, challenge, len, creds); if (len > 0) { strcpy(buf, "NTLM "); to_base64(MEM(buf, uint8_t, 5), MEM(tmp, uint8_t, 0), len, BUFSIZE-5); request->headers = hlist_mod(request->headers, "Authorization", buf, 1); free(tmp); } else { syslog(LOG_ERR, "No target info block. Cannot do NTLMv2!\n"); response->errmsg = "Invalid NTLM challenge from web server"; free(challenge); goto bailout; } } else { syslog(LOG_ERR, "Server returning invalid challenge!\n"); response->errmsg = "Invalid NTLM challenge from web server"; free(challenge); goto bailout; } free(challenge); } else { syslog(LOG_WARNING, "No challenge in WWW-Authenticate!\n"); response->errmsg = "Web server reply missing NTLM challenge"; goto bailout; } } else { goto bailout; } if (debug) printf("\nSending WWW auth...\n"); if (!headers_send(sd, request)) { goto bailout; } if (debug) printf("\nReading final server response...\n"); reset_rr_data(auth); if (!headers_recv(sd, auth)) { goto bailout; } rc = 1; if (debug) hlist_dump(auth->headers); bailout: if (rc) response = copy_rr_data(response, auth); free_rr_data(auth); free(buf); return rc; } rr_data_t direct_request(void *cdata, rr_data_t request) { rr_data_t data[2], rc = NULL; struct auth_s *tcreds = NULL; int *rsocket[2], *wsocket[2]; int w, loop, sd; char *tmp; char *hostname = NULL; int port = 0; int conn_alive = 0; int cd = ((struct thread_arg_s *)cdata)->fd; struct sockaddr_in caddr = ((struct thread_arg_s *)cdata)->addr; if (debug) printf("Direct thread processing...\n"); sd = host_connect(request->hostname, request->port); if (sd < 0) { syslog(LOG_WARNING, "Connection failed for %s:%d (%s)", request->hostname, request->port, strerror(errno)); tmp = gen_502_page(request->http, strerror(errno)); w = write(cd, tmp, strlen(tmp)); free(tmp); rc = (void *)-1; goto bailout; } /* * Now save NTLM credentials for purposes of this thread. * If web auth fails, we'll rewrite them like with NTLM-to-Basic in proxy mode. */ tcreds = dup_auth(g_creds, /* fullcopy */ 1); if (request->hostname) { hostname = strdup(request->hostname); port = request->port; } else { tmp = gen_502_page(request->http, "Invalid request URL"); w = write(cd, tmp, strlen(tmp)); free(tmp); rc = (void *)-1; goto bailout; } do { if (request) { data[0] = dup_rr_data(request); request = NULL; } else { data[0] = new_rr_data(); } data[1] = new_rr_data(); rsocket[0] = wsocket[1] = &cd; rsocket[1] = wsocket[0] = &sd; conn_alive = 0; for (loop = 0; loop < 2; ++loop) { if (data[loop]->empty) { // Isn't this the first loop with request supplied by caller? if (debug) { printf("\n******* Round %d C: %d, S: %d *******\n", loop+1, cd, sd); printf("Reading headers (%d)...\n", *rsocket[loop]); } if (!headers_recv(*rsocket[loop], data[loop])) { free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } } /* * Check whether this new request still talks to the same server as previous. * If no, return request to caller, he must decide on forward or direct * approach. */ if (loop == 0 && hostname && data[0]->hostname && (strcasecmp(hostname, data[0]->hostname) || port != data[0]->port)) { if (debug) printf("\n******* D RETURN: %s *******\n", data[0]->url); rc = dup_rr_data(data[0]); free_rr_data(data[0]); free_rr_data(data[1]); goto bailout; } if (debug) hlist_dump(data[loop]->headers); if (loop == 0 && data[0]->req) { syslog(LOG_DEBUG, "%s %s %s", inet_ntoa(caddr.sin_addr), data[0]->method, data[0]->url); /* * Convert full proxy request URL into a relative URL * Host header is already inserted by headers_recv() */ if (data[0]->rel_url) { if (data[0]->url) free(data[0]->url); data[0]->url = strdup(data[0]->rel_url); } data[0]->headers = hlist_mod(data[0]->headers, "Connection", "keep-alive", 1); data[0]->headers = hlist_del(data[0]->headers, "Proxy-Authorization"); /* * Try to get auth from client if present */ if (http_parse_basic(data[0]->headers, "Authorization", tcreds) > 0 && debug) printf("NTLM-to-basic: Credentials parsed: %s\\%s at %s\n", tcreds->domain, tcreds->user, tcreds->workstation); } /* * Is this a CONNECT request? */ if (loop == 0 && CONNECT(data[0])) { if (debug) printf("CONNECTing...\n"); data[1]->empty = 0; data[1]->req = 0; data[1]->code = 200; data[1]->msg = strdup("Connection established"); data[1]->http = strdup(data[0]->http); if (headers_send(cd, data[1])) tunnel(cd, sd); free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } if (loop == 1 && data[1]->code == 401 && hlist_subcmp_all(data[1]->headers, "WWW-Authenticate", "NTLM")) { /* * Server closing the connection after 401? * Should never happen. */ if (hlist_subcmp(data[1]->headers, "Connection", "close")) { if (debug) printf("Reconnect before WWW auth\n"); close(sd); sd = host_connect(data[0]->hostname, data[0]->port); if (sd < 0) { tmp = gen_502_page(data[0]->http, "WWW authentication reconnect failed"); w = write(cd, tmp, strlen(tmp)); free(tmp); rc = (void *)-1; goto bailout; } } if (!www_authenticate(*wsocket[0], data[0], data[1], tcreds)) { if (debug) printf("WWW auth connection error.\n"); tmp = gen_502_page(data[1]->http, data[1]->errmsg ? data[1]->errmsg : "Error during WWW-Authenticate"); w = write(cd, tmp, strlen(tmp)); free(tmp); free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } else if (data[1]->code == 401) { /* * Server giving 401 after auth? * Request basic auth */ tmp = gen_401_page(data[1]->http, data[0]->hostname, data[0]->port); w = write(cd, tmp, strlen(tmp)); free(tmp); free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } } /* * Check if we should loop for another request. Required for keep-alive * connections, client might really need a non-interrupted conversation. * * We default to keep-alive server connections, unless server explicitly * flags closing the connection or we detect a body with unknown size * (end marked by server closing). */ if (loop == 1) { conn_alive = !hlist_subcmp(data[1]->headers, "Connection", "close") && http_has_body(data[0], data[1]) != -1; if (conn_alive) { data[1]->headers = hlist_mod(data[1]->headers, "Proxy-Connection", "keep-alive", 1); data[1]->headers = hlist_mod(data[1]->headers, "Connection", "keep-alive", 1); } else { data[1]->headers = hlist_mod(data[1]->headers, "Proxy-Connection", "close", 1); rc = (void *)-1; } } if (debug) printf("Sending headers (%d)...\n", *wsocket[loop]); /* * Send headers */ if (!headers_send(*wsocket[loop], data[loop])) { free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } if (!http_body_send(*wsocket[loop], *rsocket[loop], data[0], data[1])) { free_rr_data(data[0]); free_rr_data(data[1]); rc = (void *)-1; goto bailout; } } free_rr_data(data[0]); free_rr_data(data[1]); } while (conn_alive && !so_closed(sd) && !so_closed(cd) && !serialize); bailout: if (tcreds) free(tcreds); if (hostname) free(hostname); close(sd); return rc; } void direct_tunnel(void *thread_data) { int sd, port = 0; char *pos, *hostname; int cd = ((struct thread_arg_s *)thread_data)->fd; char *thost = ((struct thread_arg_s *)thread_data)->target; struct sockaddr_in caddr = ((struct thread_arg_s *)thread_data)->addr; hostname = strdup(thost); if ((pos = strchr(hostname, ':')) != NULL) { *pos = 0; port = atoi(++pos); } sd = host_connect(hostname, port); if (sd <= 0) goto bailout; syslog(LOG_DEBUG, "%s FORWARD %s", inet_ntoa(caddr.sin_addr), thost); if (debug) printf("Portforwarding to %s for client %d...\n", thost, cd); tunnel(cd, sd); bailout: free(hostname); close(sd); close(cd); return; } cntlm-0.92.3/socket.c0000664000175000017500000001441711665255101013331 0ustar daviddavid/* * These are socket routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" extern int debug; /* * gethostbyname() wrapper. Return 1 if OK, otherwise 0. */ int so_resolv(struct in_addr *host, const char *name) { /* struct hostent *resolv; resolv = gethostbyname(name); if (!resolv) return 0; memcpy(host, resolv->h_addr_list[0], resolv->h_length); return 1; */ struct addrinfo hints, *res, *p; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; int rc = getaddrinfo(name, NULL, &hints, &res); if (rc != 0) { if (debug) printf("so_resolv: %s failed: %s (%d)\n", name, gai_strerror(rc), rc); return 0; } if (debug) printf("Resolve %s:\n", name); int addr_set = 0; for (p = res; p != NULL; p = p->ai_next) { struct sockaddr_in *ad = (struct sockaddr_in*)(p->ai_addr); if (ad == NULL) { freeaddrinfo(res); return 0; } if (!addr_set) { memcpy(host, &ad->sin_addr, sizeof(ad->sin_addr)); addr_set = 1; if (debug) printf(" -> %s\n", inet_ntoa(ad->sin_addr)); } else if (debug) printf(" %s\n", inet_ntoa(ad->sin_addr)); } freeaddrinfo(res); return 1; } /* * Connect to a host. Host is required to be resolved * in the struct in_addr already. * Returns: socket descriptor */ int so_connect(struct in_addr host, int port) { int flags, fd, rc; struct sockaddr_in saddr; // struct timeval tv; // fd_set fds; if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { if (debug) printf("so_connect: create: %s\n", strerror(errno)); return -1; } memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr = host; if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { if (debug) printf("so_connect: get flags: %s\n", strerror(errno)); close(fd); return -1; } /* NON-BLOCKING connect with timeout if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { if (debug) printf("so_connect: set non-blocking: %s\n", strerror(errno)); close(fd); return -1; } */ rc = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); /* printf("connect = %d\n", rc); if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS)) { FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 10; tv.tv_usec = 0; printf("select!\n"); rc = select(fd+1, NULL, &fds, NULL, &tv) - 1; printf("select = %d\n", rc); } */ if (rc < 0) { if (debug) printf("so_connect: %s\n", strerror(errno)); close(fd); return -1; } if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { if (debug) printf("so_connect: set blocking: %s\n", strerror(errno)); close(fd); return -1; } return fd; } /* * Bind the specified port and listen on it. * Return socket descriptor if OK, otherwise 0. */ int so_listen(int port, struct in_addr source) { struct sockaddr_in saddr; int fd; socklen_t clen; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { if (debug) printf("so_listen: new socket: %s\n", strerror(errno)); close(fd); return -1; } clen = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &clen, sizeof(clen)); memset((void *)&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = source.s_addr; if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr))) { syslog(LOG_ERR, "Cannot bind port %d: %s!\n", port, strerror(errno)); close(fd); return -1; } if (listen(fd, 5)) { close(fd); return -1; } return fd; } /* * Return 1 if data is available on the socket, * 0 if connection was closed * -1 if error (errno is set) */ int so_recvtest(int fd) { char buf; int i; #ifndef MSG_DONTWAIT unsigned int flags; flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | O_NONBLOCK); i = recv(fd, &buf, 1, MSG_PEEK); fcntl(fd, F_SETFL, flags); #else i = recv(fd, &buf, 1, MSG_DONTWAIT | MSG_PEEK); #endif return i; } /* * Return true if there are some data on the socket */ int so_dataready(int fd) { return so_recvtest(fd) > 0; } /* * Reliable way of finding out whether a connection was closed * on the remote end, without actually reading from it. */ int so_closed(int fd) { int i; if (fd == -1) return 1; i = so_recvtest(fd); return (i == 0 || (i == -1 && errno != EAGAIN && errno != ENOENT)); /* ENOENT, you ask? Perhap AIX devels could explain! :-( */ } /* * Receive a single line from the socket. This is no super-efficient * implementation, but more than we need to read in a few headers. * What's more, the data is actually recv'd from a socket buffer. * * I had to time this in comparison to recv with block read :) and * the performance was very similar. Given the fact that it keeps us * from creating a whole buffering scheme around the socket (HTTP * connection is both line and block oriented, switching back and forth), * it is actually OK. */ int so_recvln(int fd, char **buf, int *size) { int len = 0; int r = 1; char c = 0; char *tmp; while (len < *size-1 && c != '\n') { r = read(fd, &c, 1); if (r <= 0) break; (*buf)[len++] = c; /* * End of buffer, still no EOL? Resize the buffer */ if (len == *size-1 && c != '\n') { if (debug) printf("so_recvln(%d): realloc %d\n", fd, *size*2); *size *= 2; tmp = realloc(*buf, *size); if (tmp == NULL) return -1; else *buf = tmp; } } (*buf)[len] = 0; return r; } cntlm-0.92.3/utils.h0000664000175000017500000001126511646020326013202 0ustar daviddavid/* * These are helping routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _UTILS_H #define _UTILS_H #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) # include #endif #include #include #include "config/config.h" #define BUFSIZE 4096 #define MINIBUF_SIZE 50 #define VAL(var, type, offset) *((type *)(var+offset)) #define MEM(var, type, offset) (type *)(var+offset) #if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif /* #define isalnum(c) (isalpha(c) || isdigit(c)) #define isspace(c) ((c) == ' ' || (c) == '\f' || (c) == '\t' || (c) == '\r' || (c) == '\n') */ /* * Solaris doesn't have LOG_PERROR */ #ifndef LOG_PERROR # define LOG_PERROR LOG_CONS #endif /* * Two single-linked list types. First is for storing headers, * second keeps a list of finished threads or cached connections. * Each has a different set of manipulation routines. */ typedef struct hlist_s *hlist_t; struct hlist_s { char *key; char *value; int islist; struct hlist_s *next; }; typedef struct plist_s *plist_t; struct plist_s { unsigned long key; void *aux; struct plist_s *next; }; typedef enum { HLIST_NOALLOC = 0, HLIST_ALLOC } hlist_add_t; /* * Request/response data structure. Complete and parsed req/res is * kept in this. See below for (de)allocation routines. */ typedef struct rr_data_s *rr_data_t; struct rr_data_s { int req; hlist_t headers; int code; int skip_http; int body_len; int empty; int port; char *method; char *url; char *rel_url; char *hostname; char *http; char *msg; char *body; char *errmsg; }; /* * This is used in main() for passing arguments to the thread. */ struct thread_arg_s { int fd; char *target; struct sockaddr_in addr; }; extern void myexit(int rc); extern void croak(const char *msg, int console); extern plist_t plist_add(plist_t list, unsigned long key, void *aux); extern plist_t plist_del(plist_t list, unsigned long key); extern int plist_in(plist_t list, unsigned long key); extern void plist_dump(plist_t list); extern char *plist_get(plist_t list, int key); extern int plist_pop(plist_t *list, void **aux); extern int plist_count(plist_t list); extern plist_t plist_free(plist_t list); extern hlist_t hlist_add(hlist_t list, char *key, char *value, hlist_add_t allockey, hlist_add_t allocvalue); extern hlist_t hlist_dup(hlist_t list); extern hlist_t hlist_del(hlist_t list, const char *key); extern hlist_t hlist_mod(hlist_t list, char *key, char *value, int add); extern int hlist_in(hlist_t list, const char *key); extern int hlist_count(hlist_t list); extern char *hlist_get(hlist_t list, const char *key); extern int hlist_subcmp(hlist_t list, const char *key, const char *substr); extern int hlist_subcmp_all(hlist_t list, const char *key, const char *substr); extern hlist_t hlist_free(hlist_t list); extern void hlist_dump(hlist_t list); extern char *substr(const char *src, int pos, int len); extern size_t strlcpy(char *dst, const char *src, size_t siz); extern size_t strlcat(char *dst, const char *src, size_t siz); extern char *trimr(char *buf); extern char *lowercase(char *str); extern char *uppercase(char *str); extern int unicode(char **dst, char *src); extern char *new(size_t size); extern char *urlencode(const char *str); extern rr_data_t new_rr_data(void); extern rr_data_t copy_rr_data(rr_data_t dst, rr_data_t src); extern rr_data_t dup_rr_data(rr_data_t data); extern rr_data_t reset_rr_data(rr_data_t data); extern void free_rr_data(rr_data_t data); extern char *printmem(char *src, size_t len, int bitwidth); extern char *scanmem(char *src, int bitwidth); extern void to_base64(unsigned char *out, const unsigned char *in, size_t len, size_t olen); extern int from_base64(char *out, const char *in); extern long int random(void); #if config_gethostname == 1 extern int gethostname(char *name, size_t len); #endif #ifndef strdup extern char *strdup(const char *src); #endif #endif /* _UTILS_H */ cntlm-0.92.3/ntlm.c0000664000175000017500000002617411664711303013016 0ustar daviddavid/* * These are NTLM authentication routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include "ntlm.h" #include "swap.h" #include "xcrypt.h" #include "utils.h" #include "auth.h" extern int debug; static void ntlm_set_key(unsigned char *src, gl_des_ctx *context) { char key[8]; key[0] = src[0]; key[1] = ((src[0] << 7) & 0xff) | (src[1] >> 1); key[2] = ((src[1] << 6) & 0xff) | (src[2] >> 2); key[3] = ((src[2] << 5) & 0xff) | (src[3] >> 3); key[4] = ((src[3] << 4) & 0xff) | (src[4] >> 4); key[5] = ((src[4] << 3) & 0xff) | (src[5] >> 5); key[6] = ((src[5] << 2) & 0xff) | (src[6] >> 6); key[7] = (src[6] << 1) & 0xff; gl_des_setkey(context, key); } static int ntlm_calc_resp(char **dst, char *keys, char *challenge) { gl_des_ctx context; *dst = new(24 + 1); ntlm_set_key(MEM(keys, unsigned char, 0), &context); gl_des_ecb_encrypt(&context, challenge, *dst); ntlm_set_key(MEM(keys, unsigned char, 7), &context); gl_des_ecb_encrypt(&context, challenge, *dst+8); ntlm_set_key(MEM(keys, unsigned char, 14), &context); gl_des_ecb_encrypt(&context, challenge, *dst+16); return 24; } static void ntlm2_calc_resp(char **nthash, int *ntlen, char **lmhash, int *lmlen, char *passnt2, char *challenge, int tbofs, int tblen) { char *tmp, *blob, *nonce, *buf; int64_t tw; int blen; nonce = new(8 + 1); VAL(nonce, uint64_t, 0) = ((uint64_t)random() << 32) | random(); tw = ((uint64_t)time(NULL) + 11644473600LLU) * 10000000LLU; if (debug) { tmp = printmem(nonce, 8, 7); #ifdef PRId64 printf("NTLMv2:\n\t Nonce: %s\n\tTimestamp: %"PRId64"\n", tmp, tw); #else printf("NTLMv2:\n\t Nonce: %s\n\tTimestamp: %ld\n", tmp, tw); #endif free(tmp); } blob = new(4+4+8+8+4+tblen+4 + 1); VAL(blob, uint32_t, 0) = U32LE(0x00000101); VAL(blob, uint32_t, 4) = U32LE(0); VAL(blob, uint64_t, 8) = U64LE(tw); VAL(blob, uint64_t, 16) = U64LE(VAL(nonce, uint64_t, 0)); VAL(blob, uint32_t, 24) = U32LE(0); memcpy(blob+28, MEM(challenge, char, tbofs), tblen); VAL(blob, uint32_t, 28+tblen) = U32LE(0); blen = 28+tblen+4; if (0 && debug) { tmp = printmem(blob, blen, 7); printf("\t Blob: %s (%d)\n", tmp, blen); free(tmp); } *ntlen = 16+blen; *nthash = new(*ntlen + 1); buf = new(8+blen + 1); memcpy(buf, MEM(challenge, char, 24), 8); memcpy(buf+8, blob, blen); hmac_md5(passnt2, 16, buf, 8+blen, *nthash); memcpy(*nthash+16, blob, blen); free(buf); *lmlen = 24; *lmhash = new(*lmlen + 1); buf = new(16 + 1); memcpy(buf, MEM(challenge, char, 24), 8); memcpy(buf+8, nonce, 8); hmac_md5(passnt2, 16, buf, 16, *lmhash); memcpy(*lmhash+16, nonce, 8); free(buf); free(blob); free(nonce); return; } static void ntlm2sr_calc_rest(char **nthash, int *ntlen, char **lmhash, int *lmlen, char *passnt, char *challenge) { char *sess, *nonce, *buf; nonce = new(8 + 1); VAL(nonce, uint64_t, 0) = ((uint64_t)random() << 32) | random(); *lmlen = 24; *lmhash = new(*lmlen + 1); memcpy(*lmhash, nonce, 8); memset(*lmhash+8, 0, 16); buf = new(16 + 1); sess = new(16 + 1); memcpy(buf, MEM(challenge, char, 24), 8); memcpy(buf+8, nonce, 8); md5_buffer(buf, 16, sess); free(buf); *ntlen = 24; ntlm_calc_resp(nthash, passnt, sess); free(sess); free(nonce); return; } char *ntlm_hash_lm_password(char *password) { char magic[8] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; gl_des_ctx context; char *keys, *pass; keys = new(21 + 1); pass = new(14 + 1); uppercase(strncpy(pass, password, MIN(14, strlen(password)))); ntlm_set_key(MEM(pass, unsigned char, 0), &context); gl_des_ecb_encrypt(&context, magic, keys); ntlm_set_key(MEM(pass, unsigned char, 7), &context); gl_des_ecb_encrypt(&context, magic, keys+8); memset(keys+16, 0, 5); memset(pass, 0, 14); free(pass); return keys; } char *ntlm_hash_nt_password(char *password) { char *u16, *keys; int len; keys = new(21 + 1); len = unicode(&u16, password); md4_buffer(u16, len, keys); memset(keys+16, 0, 5); memset(u16, 0, len); free(u16); return keys; } char *ntlm2_hash_password(char *username, char *domain, char *password) { char *tmp, *buf, *passnt, *passnt2; int len; passnt = ntlm_hash_nt_password(password); buf = new(strlen(username)+strlen(domain) + 1); strcat(buf, username); strcat(buf, domain); uppercase(buf); len = unicode(&tmp, buf); passnt2 = new(16 + 1); hmac_md5(passnt, 16, tmp, len, passnt2); free(passnt); free(tmp); free(buf); return passnt2; } int ntlm_request(char **dst, struct auth_s *creds) { char *buf, *tmp; int dlen, hlen; uint32_t flags = 0xb206; *dst = NULL; dlen = strlen(creds->domain); hlen = strlen(creds->workstation); if (!creds->flags) { if (creds->hashntlm2) flags = 0xa208b205; else if (creds->hashnt == 2) flags = 0xa208b207; else if (creds->hashnt && creds->hashlm) flags = 0xb207; else if (creds->hashnt) flags = 0xb205; else if (creds->hashlm) flags = 0xb206; else { if (debug) { printf("You're requesting with empty auth_s?!\n"); dump_auth(creds); } return 0; } } else flags = creds->flags; if (debug) { printf("NTLM Request:\n"); printf("\t Domain: %s\n", creds->domain); printf("\t Hostname: %s\n", creds->workstation); printf("\t Flags: 0x%X\n", (int)flags); } buf = new(NTLM_BUFSIZE); memcpy(buf, "NTLMSSP\0", 8); VAL(buf, uint32_t, 8) = U32LE(1); VAL(buf, uint32_t, 12) = U32LE(flags); VAL(buf, uint16_t, 16) = U16LE(dlen); VAL(buf, uint16_t, 18) = U16LE(dlen); VAL(buf, uint32_t, 20) = U32LE(32 + hlen); VAL(buf, uint16_t, 24) = U16LE(hlen); VAL(buf, uint16_t, 26) = U16LE(hlen); VAL(buf, uint32_t, 28) = U32LE(32); tmp = uppercase(strdup(creds->workstation)); memcpy(buf+32, tmp, hlen); free(tmp); tmp = uppercase(strdup(creds->domain)); memcpy(buf+32+hlen, tmp, dlen); free(tmp); *dst = buf; return 32+dlen+hlen; } static char *printuc(char *src, int len) { char *tmp; int i; tmp = new((len+1)/2 + 1); for (i = 0; i < len/2; ++i) { tmp[i] = src[i*2]; } return tmp; } /* void dump(char *src, int len) { int i, j; char *tmp; tmp = new(len*3+4); for (i = 0; i < len; ++i) { snprintf(tmp+i*3, 4, "%0hhX ", src[i]); printf("%c ", src[i]); } printf("\n%s\n", tmp); free(tmp); } */ int ntlm_response(char **dst, char *challenge, int challen, struct auth_s *creds) { char *buf, *udomain, *uuser, *uhost, *tmp; int dlen, ulen, hlen; uint16_t tpos, tlen, ttype = -1, tbofs = 0, tblen = 0; char *lmhash = NULL, *nthash = NULL; int lmlen = 0, ntlen = 0; if (debug) { printf("NTLM Challenge:\n"); tmp = printmem(MEM(challenge, char, 24), 8, 7); printf("\tChallenge: %s (len: %d)\n", tmp, challen); free(tmp); printf("\t Flags: 0x%X\n", U32LE(VAL(challenge, uint32_t, 20))); } if (challen > 48) { tbofs = tpos = U16LE(VAL(challenge, uint16_t, 44)); while (tpos+4 <= challen && (ttype = U16LE(VAL(challenge, uint16_t, tpos)))) { tlen = U16LE(VAL(challenge, uint16_t, tpos+2)); if (tpos+4+tlen > challen) break; if (debug) { switch (ttype) { case 0x1: printf("\t Server: "); break; case 0x2: printf("\tNT domain: "); break; case 0x3: printf("\t FQDN: "); break; case 0x4: printf("\t Domain: "); break; case 0x5: printf("\t TLD: "); break; default: printf("\t %3d: ", ttype); break; } tmp = printuc(MEM(challenge, char, tpos+4), tlen); printf("%s\n", tmp); free(tmp); } tpos += 4+tlen; tblen += 4+tlen; } if (tblen && ttype == 0) tblen += 4; if (debug) { printf("\t TBofs: %d\n\t TBlen: %d\n\t ttype: %d\n", tbofs, tblen, ttype); } } if (creds->hashntlm2 && !tblen) { return 0; } if (creds->hashntlm2) { ntlm2_calc_resp(&nthash, &ntlen, &lmhash, &lmlen, creds->passntlm2, challenge, tbofs, tblen); } if (creds->hashnt == 2) { ntlm2sr_calc_rest(&nthash, &ntlen, &lmhash, &lmlen, creds->passnt, challenge); } if (creds->hashnt == 1) { ntlen = ntlm_calc_resp(&nthash, creds->passnt, MEM(challenge, char, 24)); } if (creds->hashlm) { lmlen = ntlm_calc_resp(&lmhash, creds->passlm, MEM(challenge, char, 24)); } if (creds->hashnt || creds->hashntlm2) { tmp = uppercase(strdup(creds->domain)); dlen = unicode(&udomain, tmp); free(tmp); ulen = unicode(&uuser, creds->user); tmp = uppercase(strdup(creds->workstation)); hlen = unicode(&uhost, tmp); free(tmp); } else { udomain = uppercase(strdup(creds->domain)); uuser = uppercase(strdup(creds->user)); uhost = uppercase(strdup(creds->workstation)); dlen = strlen(creds->domain); ulen = strlen(creds->user); hlen = strlen(creds->workstation); } if (debug) { printf("NTLM Response:\n"); printf("\t Hostname: '%s'\n", creds->workstation); printf("\t Domain: '%s'\n", creds->domain); printf("\t Username: '%s'\n", creds->user); if (ntlen) { tmp = printmem(nthash, ntlen, 7); printf("\t Response: '%s' (%d)\n", tmp, ntlen); free(tmp); } if (lmlen) { tmp = printmem(lmhash, lmlen, 7); printf("\t Response: '%s' (%d)\n", tmp, lmlen); free(tmp); } } buf = new(NTLM_BUFSIZE); memcpy(buf, "NTLMSSP\0", 8); VAL(buf, uint32_t, 8) = U32LE(3); /* LM */ VAL(buf, uint16_t, 12) = U16LE(lmlen); VAL(buf, uint16_t, 14) = U16LE(lmlen); VAL(buf, uint32_t, 16) = U32LE(64+dlen+ulen+hlen); /* NT */ VAL(buf, uint16_t, 20) = U16LE(ntlen); VAL(buf, uint16_t, 22) = U16LE(ntlen); VAL(buf, uint32_t, 24) = U32LE(64+dlen+ulen+hlen+lmlen); /* Domain */ VAL(buf, uint16_t, 28) = U16LE(dlen); VAL(buf, uint16_t, 30) = U16LE(dlen); VAL(buf, uint32_t, 32) = U32LE(64); /* Username */ VAL(buf, uint16_t, 36) = U16LE(ulen); VAL(buf, uint16_t, 38) = U16LE(ulen); VAL(buf, uint32_t, 40) = U32LE(64+dlen); /* Hostname */ VAL(buf, uint16_t, 44) = U16LE(hlen); VAL(buf, uint16_t, 46) = U16LE(hlen); VAL(buf, uint32_t, 48) = U32LE(64+dlen+ulen); /* Session */ VAL(buf, uint16_t, 52) = U16LE(0); VAL(buf, uint16_t, 54) = U16LE(0); VAL(buf, uint16_t, 56) = U16LE(64+dlen+ulen+hlen+lmlen+ntlen); /* Flags */ VAL(buf, uint32_t, 60) = VAL(challenge, uint32_t, 20); memcpy(MEM(buf, char, 64), udomain, dlen); memcpy(MEM(buf, char, 64+dlen), uuser, ulen); memcpy(MEM(buf, char, 64+dlen+ulen), uhost, hlen); memcpy(MEM(buf, char, 64+dlen+ulen+hlen), lmhash, lmlen); memcpy(MEM(buf, char, 64+dlen+ulen+hlen+24), nthash, ntlen); if (nthash) free(nthash); if (lmhash) free(lmhash); free(uhost); free(uuser); free(udomain); *dst = buf; return 64+dlen+ulen+hlen+lmlen+ntlen; } cntlm-0.92.3/ntlm.h0000664000175000017500000000243510712145372013015 0ustar daviddavid/* * These are NTLM authentication routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _NTLM_H #define _NTLM_H #include "xcrypt.h" #include "auth.h" #define NTLM_BUFSIZE 1024 #define NTLM_CHALLENGE_MIN 24 extern char *ntlm_hash_lm_password(char *password); extern char *ntlm_hash_nt_password(char *password); extern char *ntlm2_hash_password(char *username, char *domain, char *password); extern int ntlm_request(char **dst, struct auth_s *creds); extern int ntlm_response(char **dst, char *challenge, int challen, struct auth_s *creds); #endif /* _NTLM_H */ cntlm-0.92.3/LICENSE0000664000175000017500000004311010625070016012665 0ustar daviddavid 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) 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) year 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. cntlm-0.92.3/main.c0000664000175000017500000011666711724010052012765 0ustar daviddavid/* * This is the main module of the CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Some helping routines like linked list manipulation substr(), memory * allocation, NTLM authentication routines, etc. */ #include "config/config.h" #include "socket.h" #include "utils.h" #include "ntlm.h" #include "swap.h" #include "config.h" #include "acl.h" #include "auth.h" #include "http.h" #include "globals.h" #include "pages.h" #include "forward.h" /* code serving via parent proxy */ #include "direct.h" /* code serving directly without proxy */ #define STACK_SIZE sizeof(void *)*8*1024 /* * Global "read-only" data initialized in main(). Comments list funcs. which use * them. Having these global avoids the need to pass them to each thread and * from there again a few times to inner calls. */ int debug = 0; /* all debug printf's and possibly external modules */ struct auth_s *g_creds = NULL; /* throughout the whole module */ int quit = 0; /* sighandler() */ int ntlmbasic = 0; /* forward_request() */ int serialize = 0; int scanner_plugin = 0; long scanner_plugin_maxsize = 0; /* * List of finished threads. Each forward_request() thread adds itself to it when * finished. Main regularly joins and removes all tid's in there. */ plist_t threads_list = NULL; pthread_mutex_t threads_mtx = PTHREAD_MUTEX_INITIALIZER; /* * List of cached connections. Accessed by each thread forward_request(). */ plist_t connection_list = NULL; pthread_mutex_t connection_mtx = PTHREAD_MUTEX_INITIALIZER; /* * List of available proxies and current proxy id for proxy_connect(). */ int parent_count = 0; plist_t parent_list = NULL; /* * List of custom header substitutions, SOCKS5 proxy users and * UserAgents for the scanner plugin. */ hlist_t header_list = NULL; /* forward_request() */ hlist_t users_list = NULL; /* socks5_thread() */ plist_t scanner_agent_list = NULL; /* scanner_hook() */ plist_t noproxy_list = NULL; /* proxy_thread() */ /* * General signal handler. If in debug mode, quit immediately. */ void sighandler(int p) { if (!quit) syslog(LOG_INFO, "Signal %d received, issuing clean shutdown\n", p); else syslog(LOG_INFO, "Signal %d received, forcing shutdown\n", p); if (quit++ || debug) quit++; } /* * Parse proxy parameter and add it to the global list. */ int parent_add(char *parent, int port) { int len, i; char *proxy; proxy_t *aux; /* * Check format and parse it. */ proxy = strdup(parent); len = strlen(proxy); i = strcspn(proxy, ": "); if (i != len) { proxy[i++] = 0; while (i < len && (proxy[i] == ' ' || proxy[i] == '\t')) i++; if (i >= len) { free(proxy); return 0; } port = atoi(proxy+i); } /* * No port argument and not parsed from proxy? */ if (!port) { syslog(LOG_ERR, "Invalid proxy specification %s.\n", parent); free(proxy); myexit(1); } /* * Try to resolve proxy address * if (debug) syslog(LOG_INFO, "Resolving proxy %s...\n", proxy); if (!so_resolv(&host, proxy)) { syslog(LOG_ERR, "Cannot resolve proxy %s, discarding.\n", parent); free(proxy); return 0; } */ aux = (proxy_t *)new(sizeof(proxy_t)); strlcpy(aux->hostname, proxy, sizeof(aux->hostname)); aux->port = port; aux->resolved = 0; parent_list = plist_add(parent_list, ++parent_count, (char *)aux); free(proxy); return 1; } /* * Register and bind new proxy service port. */ void listen_add(const char *service, plist_t *list, char *spec, int gateway) { struct in_addr source; int i, p, len, port; char *tmp; len = strlen(spec); p = strcspn(spec, ":"); if (p < len-1) { tmp = substr(spec, 0, p); if (!so_resolv(&source, tmp)) { syslog(LOG_ERR, "Cannot resolve listen address %s\n", tmp); myexit(1); } free(tmp); port = atoi(tmp = spec+p+1); } else { source.s_addr = htonl(gateway ? INADDR_ANY : INADDR_LOOPBACK); port = atoi(tmp = spec); } if (!port) { syslog(LOG_ERR, "Invalid listen port %s.\n", tmp); myexit(1); } i = so_listen(port, source); if (i > 0) { *list = plist_add(*list, i, NULL); syslog(LOG_INFO, "%s listening on %s:%d\n", service, inet_ntoa(source), port); } } /* * Register a new tunnel definition, bind service port. */ void tunnel_add(plist_t *list, char *spec, int gateway) { struct in_addr source; int i, len, count, pos, port; char *field[4]; char *tmp; spec = strdup(spec); len = strlen(spec); field[0] = spec; for (count = 1, i = 0; count < 4 && i < len; ++i) if (spec[i] == ':') { spec[i] = 0; field[count++] = spec+i+1; } pos = 0; if (count == 4) { if (!so_resolv(&source, field[pos])) { syslog(LOG_ERR, "Cannot resolve tunel bind address: %s\n", field[pos]); myexit(1); } pos++; } else source.s_addr = htonl(gateway ? INADDR_ANY : INADDR_LOOPBACK); if (count-pos == 3) { port = atoi(field[pos]); if (port == 0) { syslog(LOG_ERR, "Invalid tunnel local port: %s\n", field[pos]); myexit(1); } if (!strlen(field[pos+1]) || !strlen(field[pos+2])) { syslog(LOG_ERR, "Invalid tunnel target: %s:%s\n", field[pos+1], field[pos+2]); myexit(1); } tmp = new(strlen(field[pos+1]) + strlen(field[pos+2]) + 2 + 1); strcpy(tmp, field[pos+1]); strcat(tmp, ":"); strcat(tmp, field[pos+2]); i = so_listen(port, source); if (i > 0) { *list = plist_add(*list, i, tmp); syslog(LOG_INFO, "New tunnel from %s:%d to %s\n", inet_ntoa(source), port, tmp); } else free(tmp); } else { printf("Tunnel specification incorrect ([laddress:]lport:rserver:rport).\n"); myexit(1); } free(spec); } /* * Add no-proxy hostname/IP */ plist_t noproxy_add(plist_t list, char *spec) { char *tok, *save; tok = strtok_r(spec, ", ", &save); while ( tok != NULL ) { if (debug) printf("Adding no-proxy for: '%s'\n", tok); list = plist_add(list, 0, strdup(tok)); tok = strtok_r(NULL, ", ", &save); } return list; } int noproxy_match(const char *addr) { plist_t list; list = noproxy_list; while (list) { if (list->aux && strlen(list->aux) && fnmatch(list->aux, addr, 0) == 0) { if (debug) printf("MATCH: %s (%s)\n", addr, (char *)list->aux); return 1; } else if (debug) printf(" NO: %s (%s)\n", addr, (char *)list->aux); list = list->next; } return 0; } /* * Proxy thread - decide between direct and forward based on NoProxy */ void *proxy_thread(void *thread_data) { rr_data_t request, ret; int keep_alive; /* Proxy-Connection */ int cd = ((struct thread_arg_s *)thread_data)->fd; do { ret = NULL; keep_alive = 0; if (debug) { printf("\n******* Round 1 C: %d *******\n", cd); printf("Reading headers (%d)...\n", cd); } request = new_rr_data(); if (!headers_recv(cd, request)) { free_rr_data(request); break; } do { /* * Are we being returned a request by forward_request or direct_request? */ if (ret) { free_rr_data(request); request = ret; } keep_alive = hlist_subcmp(request->headers, "Proxy-Connection", "keep-alive"); if (noproxy_match(request->hostname)) ret = direct_request(thread_data, request); else ret = forward_request(thread_data, request); if (debug) printf("proxy_thread: request rc = %p\n", (void *)ret); } while (ret != NULL && ret != (void *)-1); free_rr_data(request); /* * If client asked for proxy keep-alive, loop unless the last server response * requested (Proxy-)Connection: close. */ } while (keep_alive && ret != (void *)-1 && !serialize); /* * Add ourselves to the "threads to join" list. */ if (!serialize) { pthread_mutex_lock(&threads_mtx); threads_list = plist_add(threads_list, (unsigned long)pthread_self(), NULL); pthread_mutex_unlock(&threads_mtx); } free(thread_data); close(cd); return NULL; } /* * Tunnel/port forward thread - this method is obviously better solution than using extra * tools like "corkscrew" which after all require us for authentication and tunneling * their HTTP CONNECT in the first place. */ void *tunnel_thread(void *thread_data) { char *hostname, *pos; char *thost = ((struct thread_arg_s *)thread_data)->target; hostname = strdup(thost); if ((pos = strchr(hostname, ':')) != NULL) *pos = 0; if (noproxy_match(hostname)) direct_tunnel(thread_data); else forward_tunnel(thread_data); free(hostname); free(thread_data); /* * Add ourself to the "threads to join" list. */ pthread_mutex_lock(&threads_mtx); threads_list = plist_add(threads_list, (unsigned long)pthread_self(), NULL); pthread_mutex_unlock(&threads_mtx); return NULL; } /* * SOCKS5 thread */ void *socks5_thread(void *thread_data) { char *tmp, *thost, *tport, *uname, *upass; unsigned short port; int ver, r, c, i, w; struct auth_s *tcreds = NULL; unsigned char *bs = NULL, *auths = NULL, *addr = NULL; int found = -1; int sd = -1; int open = !hlist_count(users_list); int cd = ((struct thread_arg_s *)thread_data)->fd; struct sockaddr_in caddr = ((struct thread_arg_s *)thread_data)->addr; free(thread_data); /* * Check client's version, possibly fuck'em */ bs = (unsigned char *)new(10); thost = new(MINIBUF_SIZE); tport = new(MINIBUF_SIZE); r = read(cd, bs, 2); if (r != 2 || bs[0] != 5) goto bailout; /* * Read offered auth schemes */ c = bs[1]; auths = (unsigned char *)new(c+1); r = read(cd, auths, c); if (r != c) goto bailout; /* * Are we wide open and client is OK with no auth? */ if (open) { for (i = 0; i < c && (auths[i] || (found = 0)); ++i); } /* * If not, accept plain auth if offered */ if (found < 0) { for (i = 0; i < c && (auths[i] != 2 || !(found = 2)); ++i); } /* * If not open and no auth offered or open and auth requested, fuck'em * and complete the handshake */ if (found < 0) { bs[0] = 5; bs[1] = 0xFF; w = write(cd, bs, 2); goto bailout; } else { bs[0] = 5; bs[1] = found; w = write(cd, bs, 2); } /* * Plain auth negotiated? */ if (found != 0) { /* * Check ver and read username len */ r = read(cd, bs, 2); if (r != 2) { bs[0] = 1; bs[1] = 0xFF; /* Unsuccessful (not supported) */ w = write(cd, bs, 2); goto bailout; } c = bs[1]; /* * Read username and pass len */ uname = new(c+1); r = read(cd, uname, c+1); if (r != c+1) { free(uname); goto bailout; } i = uname[c]; uname[c] = 0; c = i; /* * Read pass */ upass = new(c+1); r = read(cd, upass, c); if (r != c) { free(upass); free(uname); goto bailout; } upass[c] = 0; /* * Check credentials against the list */ tmp = hlist_get(users_list, uname); if (!hlist_count(users_list) || (tmp && !strcmp(tmp, upass))) { bs[0] = 1; bs[1] = 0; /* Success */ } else { bs[0] = 1; bs[1] = 0xFF; /* Failed */ } /* * Send response */ w = write(cd, bs, 2); free(upass); free(uname); /* * Fuck'em if auth failed */ if (bs[1]) goto bailout; } /* * Read request type */ r = read(cd, bs, 4); if (r != 4) goto bailout; /* * Is it connect for supported address type (IPv4 or DNS)? If not, fuck'em */ if (bs[1] != 1 || (bs[3] != 1 && bs[3] != 3)) { bs[0] = 5; bs[1] = 2; /* Not allowed */ bs[2] = 0; bs[3] = 1; /* Dummy IPv4 */ memset(bs+4, 0, 6); w = write(cd, bs, 10); goto bailout; } /* * Ok, it's connect to a domain or IP * Let's read dest address */ if (bs[3] == 1) { ver = 1; /* IPv4, we know the length */ c = 4; } else if (bs[3] == 3) { ver = 2; /* FQDN, get string length */ r = read(cd, &c, 1); if (r != 1) goto bailout; } else goto bailout; addr = (unsigned char *)new(c+10 + 1); r = read(cd, addr, c); if (r != c) goto bailout; addr[c] = 0; /* * Convert the address to character string */ if (ver == 1) { sprintf(thost, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); /* It's in network byte order */ } else { strlcpy(thost, (char *)addr, MINIBUF_SIZE); } /* * Read port number and convert to host byte order int */ r = read(cd, &port, 2); if (r != 2) goto bailout; i = 0; if (noproxy_match(thost)) { sd = host_connect(thost, ntohs(port)); i = (sd >= 0); } else { sprintf(tport, "%d", ntohs(port)); strlcat(thost, ":", MINIBUF_SIZE); strlcat(thost, tport, MINIBUF_SIZE); tcreds = new_auth(); sd = proxy_connect(tcreds); if (sd >= 0) i = prepare_http_connect(sd, tcreds, thost); } /* * Direct or proxy connect? */ if (!i) { /* * Connect/tunnel failed, report */ bs[0] = 5; bs[1] = 1; /* General failure */ bs[2] = 0; bs[3] = 1; /* Dummy IPv4 */ memset(bs+4, 0, 6); w = write(cd, bs, 10); goto bailout; } else { /* * All right */ bs[0] = 5; bs[1] = 0; /* Success */ bs[2] = 0; bs[3] = 1; /* Dummy IPv4 */ memset(bs+4, 0, 6); w = write(cd, bs, 10); } syslog(LOG_DEBUG, "%s SOCKS %s", inet_ntoa(caddr.sin_addr), thost); /* * Let's give them bi-directional connection they asked for */ tunnel(cd, sd); bailout: if (addr) free(addr); if (auths) free(auths); if (thost) free(thost); if (tport) free(tport); if (bs) free(bs); if (tcreds) free(tcreds); if (sd) close(sd); close(cd); return NULL; } int main(int argc, char **argv) { char *tmp, *head; char *cpassword, *cpassntlm2, *cpassnt, *cpasslm; char *cuser, *cdomain, *cworkstation, *cuid, *cpidfile, *cauth; struct passwd *pw; struct termios termold, termnew; pthread_attr_t pattr; pthread_t pthr; hlist_t list; int i, w; int cd = 0; int help = 0; int nuid = 0; int ngid = 0; int gateway = 0; int tc = 0; int tj = 0; int interactivepwd = 0; int interactivehash = 0; int tracefile = 0; int cflags = 0; int asdaemon = 1; plist_t tunneld_list = NULL; plist_t proxyd_list = NULL; plist_t socksd_list = NULL; plist_t rules = NULL; config_t cf = NULL; char *magic_detect = NULL; g_creds = new_auth(); cuser = new(MINIBUF_SIZE); cdomain = new(MINIBUF_SIZE); cpassword = new(MINIBUF_SIZE); cpassntlm2 = new(MINIBUF_SIZE); cpassnt = new(MINIBUF_SIZE); cpasslm = new(MINIBUF_SIZE); cworkstation = new(MINIBUF_SIZE); cpidfile = new(MINIBUF_SIZE); cuid = new(MINIBUF_SIZE); cauth = new(MINIBUF_SIZE); openlog("cntlm", LOG_CONS, LOG_DAEMON); #if config_endian == 0 syslog(LOG_INFO, "Starting cntlm version " VERSION " for BIG endian\n"); #else syslog(LOG_INFO, "Starting cntlm version " VERSION " for LITTLE endian\n"); #endif while ((i = getopt(argc, argv, ":-:a:c:d:fghIl:p:r:su:vw:A:BD:F:G:HL:M:N:O:P:R:S:T:U:")) != -1) { switch (i) { case 'A': case 'D': if (!acl_add(&rules, optarg, (i == 'A' ? ACL_ALLOW : ACL_DENY))) myexit(1); break; case 'a': strlcpy(cauth, optarg, MINIBUF_SIZE); break; case 'B': ntlmbasic = 1; break; case 'c': if (!(cf = config_open(optarg))) { syslog(LOG_ERR, "Cannot access specified config file: %s\n", optarg); myexit(1); } break; case 'd': strlcpy(cdomain, optarg, MINIBUF_SIZE); break; case 'F': cflags = swap32(strtoul(optarg, &tmp, 0)); break; case 'f': asdaemon = 0; break; case 'G': if (strlen(optarg)) { scanner_plugin = 1; if (!scanner_plugin_maxsize) scanner_plugin_maxsize = 1; i = strlen(optarg) + 3; tmp = new(i); snprintf(tmp, i, "*%s*", optarg); scanner_agent_list = plist_add(scanner_agent_list, 0, tmp); } break; case 'g': gateway = 1; break; case 'H': interactivehash = 1; break; case 'I': interactivepwd = 1; break; case 'L': /* * Parse and validate the argument. * Create a listening socket for tunneling. */ tunnel_add(&tunneld_list, optarg, gateway); break; case 'l': /* * Create a listening socket for proxy function. */ listen_add("Proxy", &proxyd_list, optarg, gateway); break; case 'M': magic_detect = strdup(optarg); break; case 'N': noproxy_list = noproxy_add(noproxy_list, tmp=strdup(optarg)); free(tmp); break; case 'O': listen_add("SOCKS5 proxy", &socksd_list, optarg, gateway); break; case 'P': strlcpy(cpidfile, optarg, MINIBUF_SIZE); break; case 'p': /* * Overwrite the password parameter with '*'s to make it * invisible in "ps", /proc, etc. */ strlcpy(cpassword, optarg, MINIBUF_SIZE); for (i = strlen(optarg)-1; i >= 0; --i) optarg[i] = '*'; break; case 'R': tmp = strdup(optarg); head = strchr(tmp, ':'); if (!head) { fprintf(stderr, "Invalid username:password format for -R: %s\n", tmp); } else { head[0] = 0; users_list = hlist_add(users_list, tmp, head+1, HLIST_ALLOC, HLIST_ALLOC); } break; case 'r': if (is_http_header(optarg)) header_list = hlist_add(header_list, get_http_header_name(optarg), get_http_header_value(optarg), HLIST_NOALLOC, HLIST_NOALLOC); break; case 'S': scanner_plugin = 1; scanner_plugin_maxsize = atol(optarg); break; case 's': /* * Do not use threads - for debugging purposes only */ serialize = 1; break; case 'T': tracefile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (tracefile < 0) { fprintf(stderr, "Cannot create trace file.\n"); myexit(1); } else { printf("Redirecting all output to %s\n", optarg); dup2(tracefile, 1); dup2(tracefile, 2); printf("Cntlm debug trace, version " VERSION); #ifdef __CYGWIN__ printf(" windows/cygwin port"); #endif printf(".\nCommand line: "); for (i = 0; i < argc; ++i) printf("%s ", argv[i]); printf("\n"); } break; case 'U': strlcpy(cuid, optarg, MINIBUF_SIZE); break; case 'u': i = strcspn(optarg, "@"); if (i != strlen(optarg)) { strlcpy(cuser, optarg, MIN(MINIBUF_SIZE, i+1)); strlcpy(cdomain, optarg+i+1, MINIBUF_SIZE); } else { strlcpy(cuser, optarg, MINIBUF_SIZE); } break; case 'v': debug = 1; asdaemon = 0; openlog("cntlm", LOG_CONS | LOG_PERROR, LOG_DAEMON); break; case 'w': strlcpy(cworkstation, optarg, MINIBUF_SIZE); break; case 'h': default: help = 1; } } /* * Help requested? */ if (help) { printf("CNTLM - Accelerating NTLM Authentication Proxy version " VERSION "\n"); printf("Copyright (c) 2oo7-2o1o David Kubicek\n\n" "This program comes with NO WARRANTY, to the extent permitted by law. You\n" "may redistribute copies of it under the terms of the GNU GPL Version 2 or\n" "newer. For more information about these matters, see the file LICENSE.\n" "For copyright holders of included encryption routines see headers.\n\n"); fprintf(stderr, "Usage: %s [-AaBcDdFfgHhILlMPpSsTUuvw] [:] ...\n", argv[0]); fprintf(stderr, "\t-A
[/]\n" "\t ACL allow rule. IP or hostname, net must be a number (CIDR notation)\n"); fprintf(stderr, "\t-a ntlm | nt | lm\n" "\t Authentication type - combined NTLM, just LM, or just NT. Default NTLM.\n" "\t It is the most versatile setting and likely to work for you.\n"); fprintf(stderr, "\t-B Enable NTLM-to-basic authentication.\n"); fprintf(stderr, "\t-c \n" "\t Configuration file. Other arguments can be used as well, overriding\n" "\t config file settings.\n"); fprintf(stderr, "\t-D
[/]\n" "\t ACL deny rule. Syntax same as -A.\n"); fprintf(stderr, "\t-d \n" "\t Domain/workgroup can be set separately.\n"); fprintf(stderr, "\t-f Run in foreground, do not fork into daemon mode.\n"); fprintf(stderr, "\t-F \n" "\t NTLM authentication flags.\n"); fprintf(stderr, "\t-G \n" "\t User-Agent matching for the trans-isa-scan plugin.\n"); fprintf(stderr, "\t-g Gateway mode - listen on all interfaces, not only loopback.\n"); fprintf(stderr, "\t-H Print password hashes for use in config file (NTLMv2 needs -u and -d).\n"); fprintf(stderr, "\t-h Print this help info along with version number.\n"); fprintf(stderr, "\t-I Prompt for the password interactively.\n"); fprintf(stderr, "\t-L [:]::\n" "\t Forwarding/tunneling a la OpenSSH. Same syntax - listen on lport\n" "\t and forward all connections through the proxy to rhost:rport.\n" "\t Can be used for direct tunneling without corkscrew, etc.\n"); fprintf(stderr, "\t-l [:]\n" "\t Main listening port for the NTLM proxy.\n"); fprintf(stderr, "\t-M \n" "\t Magic autodetection of proxy's NTLM dialect.\n"); fprintf(stderr, "\t-N \"[, \"\n" "\t List of URL's to serve direcly as stand-alone proxy (e.g. '*.local')\n"); fprintf(stderr, "\t-O [:]\n" "\t Enable SOCKS5 proxy on port lport (binding to address saddr)\n"); fprintf(stderr, "\t-P \n" "\t Create a PID file upon successful start.\n"); fprintf(stderr, "\t-p \n" "\t Account password. Will not be visible in \"ps\", /proc, etc.\n"); fprintf(stderr, "\t-r \"HeaderName: value\"\n" "\t Add a header substitution. All such headers will be added/replaced\n" "\t in the client's requests.\n"); fprintf(stderr, "\t-S \n" "\t Enable automation of GFI WebMonitor ISA scanner for files < size_in_kb.\n"); fprintf(stderr, "\t-s Do not use threads, serialize all requests - for debugging only.\n"); fprintf(stderr, "\t-U \n" "\t Run as uid. It is an important security measure not to run as root.\n"); fprintf(stderr, "\t-u [@\n" "\t Some proxies require correct NetBIOS hostname.\n\n"); exit(1); } /* * More arguments on the command-line? Must be proxies. */ i = optind; while (i < argc) { tmp = strchr(argv[i], ':'); parent_add(argv[i], !tmp && i+1 < argc ? atoi(argv[i+1]) : 0); i += (!tmp ? 2 : 1); } /* * No configuration file yet? Load the default. */ #ifdef SYSCONFDIR if (!cf) { #ifdef __CYGWIN__ tmp = getenv("PROGRAMFILES(X86)"); if (tmp == NULL || strlen(tmp) == 0) tmp = getenv("PROGRAMFILES"); if (tmp == NULL) tmp = "C:\\Program Files"; head = new(MINIBUF_SIZE); strlcpy(head, tmp, MINIBUF_SIZE); strlcat(head, "\\Cntlm\\cntlm.ini", MINIBUF_SIZE); cf = config_open(head); #else cf = config_open(SYSCONFDIR "/cntlm.conf"); #endif if (debug) { if (cf) printf("Default config file opened successfully\n"); else syslog(LOG_ERR, "Could not open default config file\n"); } } #endif /* * If any configuration file was successfully opened, parse it. */ if (cf) { /* * Check if gateway mode is requested before actually binding any ports. */ tmp = new(MINIBUF_SIZE); CFG_DEFAULT(cf, "Gateway", tmp, MINIBUF_SIZE); if (!strcasecmp("yes", tmp)) gateway = 1; free(tmp); /* * Check for NTLM-to-basic settings */ tmp = new(MINIBUF_SIZE); CFG_DEFAULT(cf, "NTLMToBasic", tmp, MINIBUF_SIZE); if (!strcasecmp("yes", tmp)) ntlmbasic = 1; free(tmp); /* * Setup the rest of tunnels. */ while ((tmp = config_pop(cf, "Tunnel"))) { tunnel_add(&tunneld_list, tmp, gateway); free(tmp); } /* * Bind the rest of proxy service ports. */ while ((tmp = config_pop(cf, "Listen"))) { listen_add("Proxy", &proxyd_list, tmp, gateway); free(tmp); } /* * Bind the rest of SOCKS5 service ports. */ while ((tmp = config_pop(cf, "SOCKS5Proxy"))) { listen_add("SOCKS5 proxy", &socksd_list, tmp, gateway); free(tmp); } /* * Accept only headers not specified on the command line. * Command line has higher priority. */ while ((tmp = config_pop(cf, "Header"))) { if (is_http_header(tmp)) { head = get_http_header_name(tmp); if (!hlist_in(header_list, head)) header_list = hlist_add(header_list, head, get_http_header_value(tmp), HLIST_ALLOC, HLIST_NOALLOC); free(head); } else syslog(LOG_ERR, "Invalid header format: %s\n", tmp); free(tmp); } /* * Add the rest of parent proxies. */ while ((tmp = config_pop(cf, "Proxy"))) { parent_add(tmp, 0); free(tmp); } /* * No ACLs on the command line? Use config file. */ if (rules == NULL) { list = cf->options; while (list) { if (!(i=strcasecmp("Allow", list->key)) || !strcasecmp("Deny", list->key)) if (!acl_add(&rules, list->value, i ? ACL_DENY : ACL_ALLOW)) myexit(1); list = list->next; } while ((tmp = config_pop(cf, "Allow"))) free(tmp); while ((tmp = config_pop(cf, "Deny"))) free(tmp); } /* * Single options. */ CFG_DEFAULT(cf, "Auth", cauth, MINIBUF_SIZE); CFG_DEFAULT(cf, "Domain", cdomain, MINIBUF_SIZE); CFG_DEFAULT(cf, "Password", cpassword, MINIBUF_SIZE); CFG_DEFAULT(cf, "PassNTLMv2", cpassntlm2, MINIBUF_SIZE); CFG_DEFAULT(cf, "PassNT", cpassnt, MINIBUF_SIZE); CFG_DEFAULT(cf, "PassLM", cpasslm, MINIBUF_SIZE); CFG_DEFAULT(cf, "Username", cuser, MINIBUF_SIZE); CFG_DEFAULT(cf, "Workstation", cworkstation, MINIBUF_SIZE); tmp = new(MINIBUF_SIZE); CFG_DEFAULT(cf, "Flags", tmp, MINIBUF_SIZE); if (!cflags) cflags = swap32(strtoul(tmp, NULL, 0)); free(tmp); tmp = new(MINIBUF_SIZE); CFG_DEFAULT(cf, "ISAScannerSize", tmp, MINIBUF_SIZE); if (!scanner_plugin_maxsize && strlen(tmp)) { scanner_plugin = 1; scanner_plugin_maxsize = atoi(tmp); } free(tmp); while ((tmp = config_pop(cf, "NoProxy"))) { if (strlen(tmp)) { noproxy_list = noproxy_add(noproxy_list, tmp); } free(tmp); } while ((tmp = config_pop(cf, "SOCKS5Users"))) { head = strchr(tmp, ':'); if (!head) { syslog(LOG_ERR, "Invalid username:password format for SOCKS5User: %s\n", tmp); } else { head[0] = 0; users_list = hlist_add(users_list, tmp, head+1, HLIST_ALLOC, HLIST_ALLOC); } } /* * Add User-Agent matching patterns. */ while ((tmp = config_pop(cf, "ISAScannerAgent"))) { scanner_plugin = 1; if (!scanner_plugin_maxsize) scanner_plugin_maxsize = 1; if ((i = strlen(tmp))) { head = new(i + 3); snprintf(head, i+3, "*%s*", tmp); scanner_agent_list = plist_add(scanner_agent_list, 0, head); } free(tmp); } /* * Print out unused/unknown options. */ list = cf->options; while (list) { syslog(LOG_INFO, "Ignoring config file option: %s\n", list->key); list = list->next; } } config_close(cf); if (!interactivehash && !parent_list) croak("Parent proxy address missing.\n", interactivepwd || magic_detect); if (!interactivehash && !magic_detect && !proxyd_list) croak("No proxy service ports were successfully opened.\n", interactivepwd); /* * Set default value for the workstation. Hostname if possible. */ if (!strlen(cworkstation)) { #if config_gethostname == 1 gethostname(cworkstation, MINIBUF_SIZE); #endif if (!strlen(cworkstation)) strlcpy(cworkstation, "cntlm", MINIBUF_SIZE); syslog(LOG_INFO, "Workstation name used: %s\n", cworkstation); } /* * Parse selected NTLM hash combination. */ if (strlen(cauth)) { if (!strcasecmp("ntlm", cauth)) { g_creds->hashnt = 1; g_creds->hashlm = 1; g_creds->hashntlm2 = 0; } else if (!strcasecmp("nt", cauth)) { g_creds->hashnt = 1; g_creds->hashlm = 0; g_creds->hashntlm2 = 0; } else if (!strcasecmp("lm", cauth)) { g_creds->hashnt = 0; g_creds->hashlm = 1; g_creds->hashntlm2 = 0; } else if (!strcasecmp("ntlmv2", cauth)) { g_creds->hashnt = 0; g_creds->hashlm = 0; g_creds->hashntlm2 = 1; } else if (!strcasecmp("ntlm2sr", cauth)) { g_creds->hashnt = 2; g_creds->hashlm = 0; g_creds->hashntlm2 = 0; } else { syslog(LOG_ERR, "Unknown NTLM auth combination.\n"); myexit(1); } } if (socksd_list && !users_list) syslog(LOG_WARNING, "SOCKS5 proxy will NOT require any authentication\n"); if (!magic_detect) syslog(LOG_INFO, "Using following NTLM hashes: NTLMv2(%d) NT(%d) LM(%d)\n", g_creds->hashntlm2, g_creds->hashnt, g_creds->hashlm); if (cflags) { syslog(LOG_INFO, "Using manual NTLM flags: 0x%X\n", swap32(cflags)); g_creds->flags = cflags; } /* * Last chance to get password from the user */ if (interactivehash || magic_detect || (interactivepwd && !ntlmbasic)) { printf("Password: "); tcgetattr(0, &termold); termnew = termold; termnew.c_lflag &= ~(ISIG | ECHO); tcsetattr(0, TCSADRAIN, &termnew); tmp = fgets(cpassword, MINIBUF_SIZE, stdin); tcsetattr(0, TCSADRAIN, &termold); i = strlen(cpassword) - 1; if (cpassword[i] == '\n') { cpassword[i] = 0; if (cpassword[i - 1] == '\r') cpassword[i - 1] = 0; } printf("\n"); } /* * Convert optional PassNT, PassLM and PassNTLMv2 strings to hashes * unless plaintext pass was used, which has higher priority. * * If plain password is present, calculate its NT and LM hashes * and remove it from the memory. */ if (!strlen(cpassword)) { if (strlen(cpassntlm2)) { tmp = scanmem(cpassntlm2, 8); if (!tmp) { syslog(LOG_ERR, "Invalid PassNTLMv2 hash, terminating\n"); exit(1); } auth_memcpy(g_creds, passntlm2, tmp, 16); free(tmp); } if (strlen(cpassnt)) { tmp = scanmem(cpassnt, 8); if (!tmp) { syslog(LOG_ERR, "Invalid PassNT hash, terminating\n"); exit(1); } auth_memcpy(g_creds, passnt, tmp, 16); free(tmp); } if (strlen(cpasslm)) { tmp = scanmem(cpasslm, 8); if (!tmp) { syslog(LOG_ERR, "Invalid PassLM hash, terminating\n"); exit(1); } auth_memcpy(g_creds, passlm, tmp, 16); free(tmp); } } else { if (g_creds->hashnt || magic_detect || interactivehash) { tmp = ntlm_hash_nt_password(cpassword); auth_memcpy(g_creds, passnt, tmp, 21); free(tmp); } if (g_creds->hashlm || magic_detect || interactivehash) { tmp = ntlm_hash_lm_password(cpassword); auth_memcpy(g_creds, passlm, tmp, 21); free(tmp); } if (g_creds->hashntlm2 || magic_detect || interactivehash) { tmp = ntlm2_hash_password(cuser, cdomain, cpassword); auth_memcpy(g_creds, passntlm2, tmp, 16); free(tmp); } memset(cpassword, 0, strlen(cpassword)); } auth_strcpy(g_creds, user, cuser); auth_strcpy(g_creds, domain, cdomain); auth_strcpy(g_creds, workstation, cworkstation); free(cuser); free(cdomain); free(cworkstation); free(cpassword); free(cpassntlm2); free(cpassnt); free(cpasslm); free(cauth); /* * Try known NTLM auth combinations and print which ones work. * User can pick the best (most secure) one as his config. */ if (magic_detect) { magic_auth_detect(magic_detect); goto bailout; } if (interactivehash) { if (g_creds->passlm) { tmp = printmem(g_creds->passlm, 16, 8); printf("PassLM %s\n", tmp); free(tmp); } if (g_creds->passnt) { tmp = printmem(g_creds->passnt, 16, 8); printf("PassNT %s\n", tmp); free(tmp); } if (g_creds->passntlm2) { tmp = printmem(g_creds->passntlm2, 16, 8); printf("PassNTLMv2 %s # Only for user '%s', domain '%s'\n", tmp, g_creds->user, g_creds->domain); free(tmp); } goto bailout; } /* * If we're going to need a password, check we really have it. */ if (!ntlmbasic && ( (g_creds->hashnt && !g_creds->passnt) || (g_creds->hashlm && !g_creds->passlm) || (g_creds->hashntlm2 && !g_creds->passntlm2))) { syslog(LOG_ERR, "Parent proxy account password (or required hashes) missing.\n"); myexit(1); } /* * Ok, we are ready to rock. If daemon mode was requested, * fork and die. The child will not be group leader anymore * and can thus create a new session for itself and detach * from the controlling terminal. */ if (asdaemon) { if (debug) printf("Forking into background as requested.\n"); i = fork(); if (i == -1) { perror("Fork into background failed"); /* fork failed */ myexit(1); } else if (i) myexit(0); /* parent */ setsid(); umask(0); w = chdir("/"); i = open("/dev/null", O_RDWR); if (i >= 0) { dup2(i, 0); dup2(i, 1); dup2(i, 2); if (i > 2) close(i); } } /* * Reinit syslog logging to include our PID, after forking * it is going to be OK */ if (asdaemon) { openlog("cntlm", LOG_CONS | LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "Daemon ready"); } else { openlog("cntlm", LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON); syslog(LOG_INFO, "Cntlm ready, staying in the foreground"); } /* * Check and change UID. */ if (strlen(cuid)) { if (getuid() && geteuid()) { syslog(LOG_WARNING, "No root privileges; keeping identity %d:%d\n", getuid(), getgid()); } else { if (isdigit(cuid[0])) { nuid = atoi(cuid); ngid = nuid; if (nuid <= 0) { syslog(LOG_ERR, "Numerical uid parameter invalid\n"); myexit(1); } } else { pw = getpwnam(cuid); if (!pw || !pw->pw_uid) { syslog(LOG_ERR, "Username %s in -U is invalid\n", cuid); myexit(1); } nuid = pw->pw_uid; ngid = pw->pw_gid; } setgid(ngid); i = setuid(nuid); syslog(LOG_INFO, "Changing uid:gid to %d:%d - %s\n", nuid, ngid, strerror(errno)); if (i) { syslog(LOG_ERR, "Terminating\n"); myexit(1); } } } /* * PID file requested? Try to create one (it must not exist). * If we fail, exit with error. */ if (strlen(cpidfile)) { umask(0); cd = open(cpidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (cd < 0) { syslog(LOG_ERR, "Error creating a new PID file\n"); myexit(1); } tmp = new(50); snprintf(tmp, 50, "%d\n", getpid()); w = write(cd, tmp, strlen(tmp)); free(tmp); close(cd); } /* * Change the handler for signals recognized as clean shutdown. * When the handler is called (termination request), it signals * this news by adding 1 to the global quit variable. */ signal(SIGPIPE, SIG_IGN); signal(SIGINT, &sighandler); signal(SIGTERM, &sighandler); signal(SIGHUP, &sighandler); /* * Initialize the random number generator */ srandom(time(NULL)); /* * This loop iterates over every connection request on any of * the listening ports. We keep the number of created threads. * * We also check the "finished threads" list, threads_list, here and * free the memory of all inactive threads. Then, we update the * number of finished threads. * * The loop ends, when we were "killed" and all threads created * are finished, OR if we were killed more than once. This way, * we have a "clean" shutdown (wait for all connections to finish * after the first kill) and a "forced" one (user insists and * killed us twice). */ while (quit == 0 || (tc != tj && quit < 2)) { struct thread_arg_s *data; struct sockaddr_in caddr; struct timeval tv; socklen_t clen; fd_set set; plist_t t; int tid = 0; FD_ZERO(&set); /* * Watch for proxy ports. */ t = proxyd_list; while (t) { FD_SET(t->key, &set); t = t->next; } /* * Watch for SOCKS5 ports. */ t = socksd_list; while (t) { FD_SET(t->key, &set); t = t->next; } /* * Watch for tunneled ports. */ t = tunneld_list; while (t) { FD_SET(t->key, &set); t = t->next; } tv.tv_sec = 1; tv.tv_usec = 0; /* * Wait here for data (connection request) on any of the listening * sockets. When ready, establish the connection. For the main * port, a new proxy_thread() thread is spawned to service the HTTP * request. For tunneled ports, tunnel_thread() thread is created * and for SOCKS port, socks5_thread() is created. * * All threads are defined in forward.c, except for local proxy_thread() * which routes the request as forwarded or direct, depending on the * URL host name and NoProxy settings. */ cd = select(FD_SETSIZE, &set, NULL, NULL, &tv); if (cd > 0) { for (i = 0; i < FD_SETSIZE; ++i) { if (!FD_ISSET(i, &set)) continue; clen = sizeof(caddr); cd = accept(i, (struct sockaddr *)&caddr, (socklen_t *)&clen); if (cd < 0) { syslog(LOG_ERR, "Serious error during accept: %s\n", strerror(errno)); continue; } /* * Check main access control list. */ if (acl_check(rules, caddr.sin_addr) != ACL_ALLOW) { syslog(LOG_WARNING, "Connection denied for %s:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port)); tmp = gen_denied_page(inet_ntoa(caddr.sin_addr)); w = write(cd, tmp, strlen(tmp)); free(tmp); close(cd); continue; } /* * Log peer IP if it's not localhost * * if (debug || (gateway && caddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK))) * syslog(LOG_INFO, "Connection accepted from %s:%d\n", * inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port)); */ pthread_attr_init(&pattr); pthread_attr_setstacksize(&pattr, STACK_SIZE); #ifndef __CYGWIN__ pthread_attr_setguardsize(&pattr, 256); #endif if (plist_in(proxyd_list, i)) { data = (struct thread_arg_s *)new(sizeof(struct thread_arg_s)); data->fd = cd; data->addr = caddr; if (!serialize) tid = pthread_create(&pthr, &pattr, proxy_thread, (void *)data); else proxy_thread((void *)data); } else if (plist_in(socksd_list, i)) { data = (struct thread_arg_s *)new(sizeof(struct thread_arg_s)); data->fd = cd; data->addr = caddr; tid = pthread_create(&pthr, &pattr, socks5_thread, (void *)data); } else { data = (struct thread_arg_s *)new(sizeof(struct thread_arg_s)); data->fd = cd; data->addr = caddr; data->target = plist_get(tunneld_list, i); tid = pthread_create(&pthr, &pattr, tunnel_thread, (void *)data); } pthread_attr_destroy(&pattr); if (tid) syslog(LOG_ERR, "Serious error during pthread_create: %d\n", tid); else tc++; } } else if (cd < 0 && !quit) syslog(LOG_ERR, "Serious error during select: %s\n", strerror(errno)); if (threads_list) { pthread_mutex_lock(&threads_mtx); t = threads_list; while (t) { plist_t tmp = t->next; tid = pthread_join((pthread_t)t->key, (void *)&i); if (!tid) { tj++; if (debug) printf("Joining thread %lu; rc: %d\n", t->key, i); } else syslog(LOG_ERR, "Serious error during pthread_join: %d\n", tid); free(t); t = tmp; } threads_list = NULL; pthread_mutex_unlock(&threads_mtx); } } bailout: if (strlen(cpidfile)) unlink(cpidfile); syslog(LOG_INFO, "Terminating with %d active threads\n", tc - tj); pthread_mutex_lock(&connection_mtx); plist_free(connection_list); pthread_mutex_unlock(&connection_mtx); hlist_free(header_list); plist_free(scanner_agent_list); plist_free(noproxy_list); plist_free(tunneld_list); plist_free(proxyd_list); plist_free(socksd_list); plist_free(rules); free(cuid); free(cpidfile); free(magic_detect); free(g_creds); plist_free(parent_list); exit(0); } cntlm-0.92.3/config/0000755000175000017500000000000011725657003013135 5ustar daviddavidcntlm-0.92.3/config/endian.c0000664000175000017500000000037510662150345014542 0ustar daviddavid#include #include uint8_t num[] = { 0xEF, 0xBE }; /* * RC: 1 = LE, 0 = BE */ int main(int argc, char **argv) { int rc; rc = (*((uint16_t *)num) == 0xBEEF); printf("%s\n", rc ? "little endian" : "big endian"); return rc; } cntlm-0.92.3/config/socklen_t.c0000664000175000017500000000014410720675402015260 0ustar daviddavid#include int main(int argc, char **argv) { socklen_t len; return !!sizeof(len); } cntlm-0.92.3/config/strdup.c0000664000175000017500000000014410662150345014617 0ustar daviddavid#include int main(int argc, char **argv) { return !strcmp("hello", strdup("hello")); } cntlm-0.92.3/config/gethostname.c0000664000175000017500000000031510662150345015614 0ustar daviddavid#include int main(int argc, char **argv) { char *tmp[300]; memset(tmp, 0, sizeof(tmp)); gethostname(tmp, sizeof(tmp)-1); if (strlen(tmp)) printf("%s", tmp); return !(!strlen(tmp)); } cntlm-0.92.3/socket.h0000664000175000017500000000252011353766473013343 0ustar daviddavid/* * These are socket routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _SOCKET_H #define _SOCKET_H #include #include #include "config/config.h" #if config_socklen_t != 1 #define socklen_t uint32_t #endif #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 #endif extern int so_resolv(struct in_addr *host, const char *name); extern int so_connect(struct in_addr host, int port); extern int so_listen(int port, struct in_addr source); extern int so_dataready(int fd); extern int so_closed(int fd); extern int so_recvln(int fd, char **buf, int *size); #endif /* _SOCKET_H */ cntlm-0.92.3/xcrypt.c0000664000175000017500000012521710664307376013405 0ustar daviddavid/* des.c --- DES and Triple-DES encryption/decryption Algorithm * Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * Free Software Foundation, Inc. * * This file 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, or (at your * option) any later version. * * This file 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 file; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * ---------------------------------------------------------------------- * Functions to compute MD4 message digest of files or memory blocks. * according to the definition of MD4 in RFC 1320 from April 1992. Copyright * (C) 1995,1996,1997,1999,2000,2001,2002,2003,2005,2006 Free Software * Foundation, Inc. * * 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "xcrypt.h" #include "swap.h" #define SWAP(n) U32LE(n) #define BLOCKSIZE 4096 #if BLOCKSIZE % 64 != 0 # error "invalid BLOCKSIZE" #endif /* * To check alignment gcc has an appropriate operator. Other compilers don't. */ # if __GNUC__ >= 2 # define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint32_t) != 0) # else # define alignof(type) offsetof (struct { char c; type x; }, x) # define UNALIGNED_P(p) (((size_t) p) % alignof (uint32_t) != 0) # endif # define MD4_DIGEST_SIZE 16 /* MD4 round constants */ #define K1 0x5a827999 #define K2 0x6ed9eba1 /* Round functions. */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define rol(x, n) (((x) << (n)) | ((uint32_t) (x) >> (32 - (n)))) #define R1(a,b,c,d,k,s) a=rol(a+F(b,c,d)+x[k],s); #define R2(a,b,c,d,k,s) a=rol(a+G(b,c,d)+x[k]+K1,s); #define R3(a,b,c,d,k,s) a=rol(a+H(b,c,d)+x[k]+K2,s); /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (RFC 1320, 3.1: Step 1) */ static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; /* * The s-box values are permuted according to the 'primitive function P' * and are rotated one bit to the left. */ static const uint32_t sbox1[64] = { 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, 0x00010400, 0x00000000, 0x01010004 }; static const uint32_t sbox2[64] = { 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, 0x80100020, 0x80108020, 0x00108000 }; static const uint32_t sbox3[64] = { 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, 0x00000008, 0x08020008, 0x00020200 }; static const uint32_t sbox4[64] = { 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002000, 0x00802080 }; static const uint32_t sbox5[64] = { 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, 0x40080000, 0x02080100, 0x40000100 }; static const uint32_t sbox6[64] = { 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, 0x20000000, 0x00400010, 0x20004010 }; static const uint32_t sbox7[64] = { 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, 0x04000800, 0x00000800, 0x00200002 }; static const uint32_t sbox8[64] = { 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, 0x00040040, 0x10000000, 0x10041000 }; /* * These two tables are part of the 'permuted choice 1' function. * In this implementation several speed improvements are done. */ static const uint32_t leftkey_swap[16] = { 0x00000000, 0x00000001, 0x00000100, 0x00000101, 0x00010000, 0x00010001, 0x00010100, 0x00010101, 0x01000000, 0x01000001, 0x01000100, 0x01000101, 0x01010000, 0x01010001, 0x01010100, 0x01010101 }; static const uint32_t rightkey_swap[16] = { 0x00000000, 0x01000000, 0x00010000, 0x01010000, 0x00000100, 0x01000100, 0x00010100, 0x01010100, 0x00000001, 0x01000001, 0x00010001, 0x01010001, 0x00000101, 0x01000101, 0x00010101, 0x01010101, }; /* * Numbers of left shifts per round for encryption subkeys. To * calculate the decryption subkeys we just reverse the ordering of * the calculated encryption subkeys, so there is no need for a * decryption rotate tab. */ static const unsigned char encrypt_rotate_tab[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; /* * Table with weak DES keys sorted in ascending order. In DES there * are 64 known keys which are weak. They are weak because they * produce only one, two or four different subkeys in the subkey * scheduling process. The keys in this table have all their parity * bits cleared. */ static const unsigned char weak_keys[64][8] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*w */ {0x00, 0x00, 0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e}, {0x00, 0x00, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0}, {0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe}, {0x00, 0x1e, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x0e}, /*sw */ {0x00, 0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e, 0x00}, {0x00, 0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0, 0xfe}, {0x00, 0x1e, 0xfe, 0xe0, 0x00, 0x0e, 0xfe, 0xf0}, {0x00, 0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf0}, /*sw */ {0x00, 0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e, 0xfe}, {0x00, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0, 0x00}, {0x00, 0xe0, 0xfe, 0x1e, 0x00, 0xf0, 0xfe, 0x0e}, {0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe}, /*sw */ {0x00, 0xfe, 0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0}, {0x00, 0xfe, 0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e}, {0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00}, {0x1e, 0x00, 0x00, 0x1e, 0x0e, 0x00, 0x00, 0x0e}, {0x1e, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x0e, 0x00}, /*sw */ {0x1e, 0x00, 0xe0, 0xfe, 0x0e, 0x00, 0xf0, 0xfe}, {0x1e, 0x00, 0xfe, 0xe0, 0x0e, 0x00, 0xfe, 0xf0}, {0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e, 0x00, 0x00}, {0x1e, 0x1e, 0x1e, 0x1e, 0x0e, 0x0e, 0x0e, 0x0e}, /*w */ {0x1e, 0x1e, 0xe0, 0xe0, 0x0e, 0x0e, 0xf0, 0xf0}, {0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e, 0xfe, 0xfe}, {0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0, 0x00, 0xfe}, {0x1e, 0xe0, 0x1e, 0xe0, 0x0e, 0xf0, 0x0e, 0xf0}, /*sw */ {0x1e, 0xe0, 0xe0, 0x1e, 0x0e, 0xf0, 0xf0, 0x0e}, {0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0, 0xfe, 0x00}, {0x1e, 0xfe, 0x00, 0xe0, 0x0e, 0xfe, 0x00, 0xf0}, {0x1e, 0xfe, 0x1e, 0xfe, 0x0e, 0xfe, 0x0e, 0xfe}, /*sw */ {0x1e, 0xfe, 0xe0, 0x00, 0x0e, 0xfe, 0xf0, 0x00}, {0x1e, 0xfe, 0xfe, 0x1e, 0x0e, 0xfe, 0xfe, 0x0e}, {0xe0, 0x00, 0x00, 0xe0, 0xf0, 0x00, 0x00, 0xf0}, {0xe0, 0x00, 0x1e, 0xfe, 0xf0, 0x00, 0x0e, 0xfe}, {0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf0, 0x00}, /*sw */ {0xe0, 0x00, 0xfe, 0x1e, 0xf0, 0x00, 0xfe, 0x0e}, {0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e, 0x00, 0xfe}, {0xe0, 0x1e, 0x1e, 0xe0, 0xf0, 0x0e, 0x0e, 0xf0}, {0xe0, 0x1e, 0xe0, 0x1e, 0xf0, 0x0e, 0xf0, 0x0e}, /*sw */ {0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e, 0xfe, 0x00}, {0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00}, {0xe0, 0xe0, 0x1e, 0x1e, 0xf0, 0xf0, 0x0e, 0x0e}, {0xe0, 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0}, /*w */ {0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0, 0xfe, 0xfe}, {0xe0, 0xfe, 0x00, 0x1e, 0xf0, 0xfe, 0x00, 0x0e}, {0xe0, 0xfe, 0x1e, 0x00, 0xf0, 0xfe, 0x0e, 0x00}, {0xe0, 0xfe, 0xe0, 0xfe, 0xf0, 0xfe, 0xf0, 0xfe}, /*sw */ {0xe0, 0xfe, 0xfe, 0xe0, 0xf0, 0xfe, 0xfe, 0xf0}, {0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe}, {0xfe, 0x00, 0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0}, {0xfe, 0x00, 0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e}, {0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00}, /*sw */ {0xfe, 0x1e, 0x00, 0xe0, 0xfe, 0x0e, 0x00, 0xf0}, {0xfe, 0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e, 0xfe}, {0xfe, 0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0, 0x00}, {0xfe, 0x1e, 0xfe, 0x1e, 0xfe, 0x0e, 0xfe, 0x0e}, /*sw */ {0xfe, 0xe0, 0x00, 0x1e, 0xfe, 0xf0, 0x00, 0x0e}, {0xfe, 0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e, 0x00}, {0xfe, 0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0, 0xfe}, {0xfe, 0xe0, 0xfe, 0xe0, 0xfe, 0xf0, 0xfe, 0xf0}, /*sw */ {0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00}, {0xfe, 0xfe, 0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e}, {0xfe, 0xfe, 0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0}, {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe} /*w */ }; bool gl_des_is_weak_key (const char * key) { char work[8]; int i, left, right, middle, cmp_result; /* clear parity bits */ for (i = 0; i < 8; ++i) work[i] = ((unsigned char)key[i]) & 0xfe; /* binary search in the weak key table */ left = 0; right = 63; while (left <= right) { middle = (left + right) / 2; if (!(cmp_result = memcmp (work, weak_keys[middle], 8))) return -1; if (cmp_result > 0) left = middle + 1; else right = middle - 1; } return 0; } /* * Macro to swap bits across two words. */ #define DO_PERMUTATION(a, temp, b, offset, mask) \ temp = ((a>>offset) ^ b) & mask; \ b ^= temp; \ a ^= temp<> 31); \ temp = (left ^ right) & 0xaaaaaaaa; \ right ^= temp; \ left ^= temp; \ left = (left << 1) | (left >> 31); /* * The 'inverse initial permutation'. */ #define FINAL_PERMUTATION(left, temp, right) \ left = (left << 31) | (left >> 1); \ temp = (left ^ right) & 0xaaaaaaaa; \ left ^= temp; \ right ^= temp; \ right = (right << 31) | (right >> 1); \ DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff) \ DO_PERMUTATION(right, temp, left, 2, 0x33333333) \ DO_PERMUTATION(left, temp, right, 16, 0x0000ffff) \ DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f) /* * A full DES round including 'expansion function', 'sbox substitution' * and 'primitive function P' but without swapping the left and right word. * Please note: The data in 'from' and 'to' is already rotated one bit to * the left, done in the initial permutation. */ #define DES_ROUND(from, to, work, subkey) \ work = from ^ *subkey++; \ to ^= sbox8[ work & 0x3f ]; \ to ^= sbox6[ (work>>8) & 0x3f ]; \ to ^= sbox4[ (work>>16) & 0x3f ]; \ to ^= sbox2[ (work>>24) & 0x3f ]; \ work = ((from << 28) | (from >> 4)) ^ *subkey++; \ to ^= sbox7[ work & 0x3f ]; \ to ^= sbox5[ (work>>8) & 0x3f ]; \ to ^= sbox3[ (work>>16) & 0x3f ]; \ to ^= sbox1[ (work>>24) & 0x3f ]; /* * Macros to convert 8 bytes from/to 32bit words. */ #define READ_64BIT_DATA(data, left, right) \ left = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; \ right = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]; #define WRITE_64BIT_DATA(data, left, right) \ data[0] = (left >> 24) &0xff; data[1] = (left >> 16) &0xff; \ data[2] = (left >> 8) &0xff; data[3] = left &0xff; \ data[4] = (right >> 24) &0xff; data[5] = (right >> 16) &0xff; \ data[6] = (right >> 8) &0xff; data[7] = right &0xff; /* * des_key_schedule(): Calculate 16 subkeys pairs (even/odd) for * 16 encryption rounds. * To calculate subkeys for decryption the caller * have to reorder the generated subkeys. * * rawkey: 8 Bytes of key data * subkey: Array of at least 32 uint32_ts. Will be filled * with calculated subkeys. * */ static void des_key_schedule (const char * _rawkey, uint32_t * subkey) { const unsigned char *rawkey = (const unsigned char *) _rawkey; uint32_t left, right, work; int round; READ_64BIT_DATA (rawkey, left, right) DO_PERMUTATION (right, work, left, 4, 0x0f0f0f0f) DO_PERMUTATION (right, work, left, 0, 0x10101010) left = ((leftkey_swap[(left >> 0) & 0xf] << 3) | (leftkey_swap[(left >> 8) & 0xf] << 2) | (leftkey_swap[(left >> 16) & 0xf] << 1) | (leftkey_swap[(left >> 24) & 0xf]) | (leftkey_swap[(left >> 5) & 0xf] << 7) | (leftkey_swap[(left >> 13) & 0xf] << 6) | (leftkey_swap[(left >> 21) & 0xf] << 5) | (leftkey_swap[(left >> 29) & 0xf] << 4)); left &= 0x0fffffff; right = ((rightkey_swap[(right >> 1) & 0xf] << 3) | (rightkey_swap[(right >> 9) & 0xf] << 2) | (rightkey_swap[(right >> 17) & 0xf] << 1) | (rightkey_swap[(right >> 25) & 0xf]) | (rightkey_swap[(right >> 4) & 0xf] << 7) | (rightkey_swap[(right >> 12) & 0xf] << 6) | (rightkey_swap[(right >> 20) & 0xf] << 5) | (rightkey_swap[(right >> 28) & 0xf] << 4)); right &= 0x0fffffff; for (round = 0; round < 16; ++round) { left = ((left << encrypt_rotate_tab[round]) | (left >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff; right = ((right << encrypt_rotate_tab[round]) | (right >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff; *subkey++ = (((left << 4) & 0x24000000) | ((left << 28) & 0x10000000) | ((left << 14) & 0x08000000) | ((left << 18) & 0x02080000) | ((left << 6) & 0x01000000) | ((left << 9) & 0x00200000) | ((left >> 1) & 0x00100000) | ((left << 10) & 0x00040000) | ((left << 2) & 0x00020000) | ((left >> 10) & 0x00010000) | ((right >> 13) & 0x00002000) | ((right >> 4) & 0x00001000) | ((right << 6) & 0x00000800) | ((right >> 1) & 0x00000400) | ((right >> 14) & 0x00000200) | (right & 0x00000100) | ((right >> 5) & 0x00000020) | ((right >> 10) & 0x00000010) | ((right >> 3) & 0x00000008) | ((right >> 18) & 0x00000004) | ((right >> 26) & 0x00000002) | ((right >> 24) & 0x00000001)); *subkey++ = (((left << 15) & 0x20000000) | ((left << 17) & 0x10000000) | ((left << 10) & 0x08000000) | ((left << 22) & 0x04000000) | ((left >> 2) & 0x02000000) | ((left << 1) & 0x01000000) | ((left << 16) & 0x00200000) | ((left << 11) & 0x00100000) | ((left << 3) & 0x00080000) | ((left >> 6) & 0x00040000) | ((left << 15) & 0x00020000) | ((left >> 4) & 0x00010000) | ((right >> 2) & 0x00002000) | ((right << 8) & 0x00001000) | ((right >> 14) & 0x00000808) | ((right >> 9) & 0x00000400) | ((right) & 0x00000200) | ((right << 7) & 0x00000100) | ((right >> 7) & 0x00000020) | ((right >> 3) & 0x00000011) | ((right << 2) & 0x00000004) | ((right >> 21) & 0x00000002)); } } void gl_des_setkey (gl_des_ctx *ctx, const char * key) { int i; des_key_schedule (key, ctx->encrypt_subkeys); for (i = 0; i < 32; i += 2) { ctx->decrypt_subkeys[i] = ctx->encrypt_subkeys[30 - i]; ctx->decrypt_subkeys[i + 1] = ctx->encrypt_subkeys[31 - i]; } } bool gl_des_makekey (gl_des_ctx *ctx, const char * key, size_t keylen) { if (keylen != 8) return false; gl_des_setkey (ctx, key); return !gl_des_is_weak_key (key); } void gl_des_ecb_crypt (gl_des_ctx *ctx, const char * _from, char * _to, int mode) { const unsigned char *from = (const unsigned char *) _from; unsigned char *to = (unsigned char *) _to; uint32_t left, right, work; uint32_t *keys; keys = mode ? ctx->decrypt_subkeys : ctx->encrypt_subkeys; READ_64BIT_DATA (from, left, right) INITIAL_PERMUTATION (left, work, right) DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) FINAL_PERMUTATION (right, work, left) WRITE_64BIT_DATA (to, right, left) } /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. */ void md4_process_block (const void *buffer, size_t len, struct md4_ctx *ctx) { const uint32_t *words = buffer; size_t nwords = len / sizeof (uint32_t); const uint32_t *endp = words + nwords; uint32_t x[16]; uint32_t A = ctx->A; uint32_t B = ctx->B; uint32_t C = ctx->C; uint32_t D = ctx->D; /* First increment the byte count. RFC 1320 specifies the possible length of the file up to 2^64 bits. Here we only compute the number of bytes. Do a double word increment. */ ctx->total[0] += len; if (ctx->total[0] < len) ++ctx->total[1]; /* Process all bytes in the buffer with 64 bytes in each round of the loop. */ while (words < endp) { int t; for (t = 0; t < 16; t++) { x[t] = SWAP (*words); words++; } /* Round 1. */ R1 (A, B, C, D, 0, 3); R1 (D, A, B, C, 1, 7); R1 (C, D, A, B, 2, 11); R1 (B, C, D, A, 3, 19); R1 (A, B, C, D, 4, 3); R1 (D, A, B, C, 5, 7); R1 (C, D, A, B, 6, 11); R1 (B, C, D, A, 7, 19); R1 (A, B, C, D, 8, 3); R1 (D, A, B, C, 9, 7); R1 (C, D, A, B, 10, 11); R1 (B, C, D, A, 11, 19); R1 (A, B, C, D, 12, 3); R1 (D, A, B, C, 13, 7); R1 (C, D, A, B, 14, 11); R1 (B, C, D, A, 15, 19); /* Round 2. */ R2 (A, B, C, D, 0, 3); R2 (D, A, B, C, 4, 5); R2 (C, D, A, B, 8, 9); R2 (B, C, D, A, 12, 13); R2 (A, B, C, D, 1, 3); R2 (D, A, B, C, 5, 5); R2 (C, D, A, B, 9, 9); R2 (B, C, D, A, 13, 13); R2 (A, B, C, D, 2, 3); R2 (D, A, B, C, 6, 5); R2 (C, D, A, B, 10, 9); R2 (B, C, D, A, 14, 13); R2 (A, B, C, D, 3, 3); R2 (D, A, B, C, 7, 5); R2 (C, D, A, B, 11, 9); R2 (B, C, D, A, 15, 13); /* Round 3. */ R3 (A, B, C, D, 0, 3); R3 (D, A, B, C, 8, 9); R3 (C, D, A, B, 4, 11); R3 (B, C, D, A, 12, 15); R3 (A, B, C, D, 2, 3); R3 (D, A, B, C, 10, 9); R3 (C, D, A, B, 6, 11); R3 (B, C, D, A, 14, 15); R3 (A, B, C, D, 1, 3); R3 (D, A, B, C, 9, 9); R3 (C, D, A, B, 5, 11); R3 (B, C, D, A, 13, 15); R3 (A, B, C, D, 3, 3); R3 (D, A, B, C, 11, 9); R3 (C, D, A, B, 7, 11); R3 (B, C, D, A, 15, 15); A = ctx->A += A; B = ctx->B += B; C = ctx->C += C; D = ctx->D += D; } } /* Initialize structure containing state of computation. (RFC 1320, 3.3: Step 3) */ void md4_init_ctx (struct md4_ctx *ctx) { ctx->A = 0x67452301; ctx->B = 0xefcdab89; ctx->C = 0x98badcfe; ctx->D = 0x10325476; ctx->total[0] = ctx->total[1] = 0; ctx->buflen = 0; } /* Put result from CTX in first 16 bytes following RESBUF. The result must be in little endian byte order. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ void * md4_read_ctx (const struct md4_ctx *ctx, void *resbuf) { ((uint32_t *) resbuf)[0] = SWAP (ctx->A); ((uint32_t *) resbuf)[1] = SWAP (ctx->B); ((uint32_t *) resbuf)[2] = SWAP (ctx->C); ((uint32_t *) resbuf)[3] = SWAP (ctx->D); return resbuf; } /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ void * md4_finish_ctx (struct md4_ctx *ctx, void *resbuf) { /* Take yet unprocessed bytes into account. */ uint32_t bytes = ctx->buflen; size_t pad; /* Now count remaining bytes. */ ctx->total[0] += bytes; if (ctx->total[0] < bytes) ++ctx->total[1]; pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; memcpy (&((char*)ctx->buffer)[bytes], fillbuf, pad); /* Put the 64-bit file length in *bits* at the end of the buffer. */ ctx->buffer[(bytes + pad) / 4] = SWAP (ctx->total[0] << 3); ctx->buffer[(bytes + pad) / 4 + 1] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); /* Process last bytes. */ md4_process_block (ctx->buffer, bytes + pad + 8, ctx); return md4_read_ctx (ctx, resbuf); } void md4_process_bytes (const void *buffer, size_t len, struct md4_ctx *ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ if (ctx->buflen != 0) { size_t left_over = ctx->buflen; size_t add = 128 - left_over > len ? len : 128 - left_over; memcpy (&((char*)ctx->buffer)[left_over], buffer, add); ctx->buflen += add; if (ctx->buflen > 64) { md4_process_block (ctx->buffer, ctx->buflen & ~63, ctx); ctx->buflen &= 63; /* The regions in the following copy operation cannot overlap. */ memcpy (ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~63], ctx->buflen); } buffer = (const char *) buffer + add; len -= add; } /* Process available complete blocks. */ if (len >= 64) { #if !_STRING_ARCH_unaligned if (UNALIGNED_P (buffer)) while (len > 64) { md4_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); buffer = (const char *) buffer + 64; len -= 64; } else #endif { md4_process_block (buffer, len & ~63, ctx); buffer = (const char *) buffer + (len & ~63); len &= 63; } } /* Move remaining bytes in internal buffer. */ if (len > 0) { size_t left_over = ctx->buflen; memcpy (&((char*)ctx->buffer)[left_over], buffer, len); left_over += len; if (left_over >= 64) { md4_process_block (ctx->buffer, 64, ctx); left_over -= 64; memcpy (ctx->buffer, &ctx->buffer[16], left_over); } ctx->buflen = left_over; } } /* Compute MD4 message digest for bytes read from STREAM. The resulting message digest number will be written into the 16 bytes beginning at RESBLOCK. */ int md4_stream (FILE * stream, void *resblock) { struct md4_ctx ctx; char buffer[BLOCKSIZE + 72]; size_t sum; /* Initialize the computation context. */ md4_init_ctx (&ctx); /* Iterate over full file contents. */ while (1) { /* We read the file in blocks of BLOCKSIZE bytes. One call of the computation function processes the whole buffer so that with the next round of the loop another block can be read. */ size_t n; sum = 0; /* Read block. Take care for partial reads. */ while (1) { n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); sum += n; if (sum == BLOCKSIZE) break; if (n == 0) { /* Check for the error flag IFF N == 0, so that we don't exit the loop after a partial read due to e.g., EAGAIN or EWOULDBLOCK. */ if (ferror (stream)) return 1; goto process_partial_block; } /* We've read at least one byte, so ignore errors. But always check for EOF, since feof may be true even though N > 0. Otherwise, we could end up calling fread after EOF. */ if (feof (stream)) goto process_partial_block; } /* Process buffer with BLOCKSIZE bytes. Note that BLOCKSIZE % 64 == 0 */ md4_process_block (buffer, BLOCKSIZE, &ctx); } process_partial_block:; /* Process any remaining bytes. */ if (sum > 0) md4_process_bytes (buffer, sum, &ctx); /* Construct result in desired memory. */ md4_finish_ctx (&ctx, resblock); return 0; } /* Compute MD4 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ void * md4_buffer (const char *buffer, size_t len, void *resblock) { struct md4_ctx ctx; /* Initialize the computation context. */ md4_init_ctx (&ctx); /* Process whole buffer but last len % 64 bytes. */ md4_process_bytes (buffer, len, &ctx); /* Put result in desired memory area. */ return md4_finish_ctx (&ctx, resblock); } void * memxor (void *dest, const void *src, size_t n) { char const *s = src; char *d = dest; for (; n > 0; n--) *d++ ^= *s++; return dest; } int hmac_md5 (const void *key, size_t keylen, const void *in, size_t inlen, void *resbuf) { struct md5_ctx inner; struct md5_ctx outer; char optkeybuf[16]; char block[64]; char innerhash[16]; /* Reduce the key's size, so that it becomes <= 64 bytes large. */ if (keylen > 64) { struct md5_ctx keyhash; md5_init_ctx (&keyhash); md5_process_bytes (key, keylen, &keyhash); md5_finish_ctx (&keyhash, optkeybuf); key = optkeybuf; keylen = 16; } /* Compute INNERHASH from KEY and IN. */ md5_init_ctx (&inner); memset (block, IPAD, sizeof (block)); memxor (block, key, keylen); md5_process_block (block, 64, &inner); md5_process_bytes (in, inlen, &inner); md5_finish_ctx (&inner, innerhash); /* Compute result from KEY and INNERHASH. */ md5_init_ctx (&outer); memset (block, OPAD, sizeof (block)); memxor (block, key, keylen); md5_process_block (block, 64, &outer); md5_process_bytes (innerhash, 16, &outer); md5_finish_ctx (&outer, resbuf); return 0; } /* Initialize structure containing state of computation. (RFC 1321, 3.3: Step 3) */ void md5_init_ctx (struct md5_ctx *ctx) { ctx->A = 0x67452301; ctx->B = 0xefcdab89; ctx->C = 0x98badcfe; ctx->D = 0x10325476; ctx->total[0] = ctx->total[1] = 0; ctx->buflen = 0; } /* Put result from CTX in first 16 bytes following RESBUF. The result must be in little endian byte order. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32-bit value. */ void * md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) { ((uint32_t *) resbuf)[0] = SWAP (ctx->A); ((uint32_t *) resbuf)[1] = SWAP (ctx->B); ((uint32_t *) resbuf)[2] = SWAP (ctx->C); ((uint32_t *) resbuf)[3] = SWAP (ctx->D); return resbuf; } /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32-bit value. */ void * md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) { /* Take yet unprocessed bytes into account. */ uint32_t bytes = ctx->buflen; size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; /* Now count remaining bytes. */ ctx->total[0] += bytes; if (ctx->total[0] < bytes) ++ctx->total[1]; /* Put the 64-bit file length in *bits* at the end of the buffer. */ ctx->buffer[size - 2] = SWAP (ctx->total[0] << 3); ctx->buffer[size - 1] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); /* Process last bytes. */ md5_process_block (ctx->buffer, size * 4, ctx); return md5_read_ctx (ctx, resbuf); } /* Compute MD5 message digest for bytes read from STREAM. The resulting message digest number will be written into the 16 bytes beginning at RESBLOCK. */ int md5_stream (FILE *stream, void *resblock) { struct md5_ctx ctx; char buffer[BLOCKSIZE + 72]; size_t sum; /* Initialize the computation context. */ md5_init_ctx (&ctx); /* Iterate over full file contents. */ while (1) { /* We read the file in blocks of BLOCKSIZE bytes. One call of the computation function processes the whole buffer so that with the next round of the loop another block can be read. */ size_t n; sum = 0; /* Read block. Take care for partial reads. */ while (1) { n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); sum += n; if (sum == BLOCKSIZE) break; if (n == 0) { /* Check for the error flag IFF N == 0, so that we don't exit the loop after a partial read due to e.g., EAGAIN or EWOULDBLOCK. */ if (ferror (stream)) return 1; goto process_partial_block; } /* We've read at least one byte, so ignore errors. But always check for EOF, since feof may be true even though N > 0. Otherwise, we could end up calling fread after EOF. */ if (feof (stream)) goto process_partial_block; } /* Process buffer with BLOCKSIZE bytes. Note that BLOCKSIZE % 64 == 0 */ md5_process_block (buffer, BLOCKSIZE, &ctx); } process_partial_block: /* Process any remaining bytes. */ if (sum > 0) md5_process_bytes (buffer, sum, &ctx); /* Construct result in desired memory. */ md5_finish_ctx (&ctx, resblock); return 0; } /* Compute MD5 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ void * md5_buffer (const char *buffer, size_t len, void *resblock) { struct md5_ctx ctx; /* Initialize the computation context. */ md5_init_ctx (&ctx); /* Process whole buffer but last len % 64 bytes. */ md5_process_bytes (buffer, len, &ctx); /* Put result in desired memory area. */ return md5_finish_ctx (&ctx, resblock); } void md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ if (ctx->buflen != 0) { size_t left_over = ctx->buflen; size_t add = 128 - left_over > len ? len : 128 - left_over; memcpy (&((char *) ctx->buffer)[left_over], buffer, add); ctx->buflen += add; if (ctx->buflen > 64) { md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); ctx->buflen &= 63; /* The regions in the following copy operation cannot overlap. */ memcpy (ctx->buffer, &((char *) ctx->buffer)[(left_over + add) & ~63], ctx->buflen); } buffer = (const char *) buffer + add; len -= add; } /* Process available complete blocks. */ if (len >= 64) { #if !_STRING_ARCH_unaligned if (UNALIGNED_P (buffer)) while (len > 64) { md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); buffer = (const char *) buffer + 64; len -= 64; } else #endif { md5_process_block (buffer, len & ~63, ctx); buffer = (const char *) buffer + (len & ~63); len &= 63; } } /* Move remaining bytes in internal buffer. */ if (len > 0) { size_t left_over = ctx->buflen; memcpy (&((char *) ctx->buffer)[left_over], buffer, len); left_over += len; if (left_over >= 64) { md5_process_block (ctx->buffer, 64, ctx); left_over -= 64; memcpy (ctx->buffer, &ctx->buffer[16], left_over); } ctx->buflen = left_over; } } /* These are the four functions used in the four steps of the MD5 algorithm and defined in the RFC 1321. The first function is a little bit optimized (as found in Colin Plumbs public domain implementation). */ /* #define FF(b, c, d) ((b & c) | (~b & d)) */ #define FF(b, c, d) (d ^ (b & (c ^ d))) #define FG(b, c, d) FF (d, b, c) #define FH(b, c, d) (b ^ c ^ d) #define FI(b, c, d) (c ^ (b | ~d)) /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. */ void md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx) { uint32_t correct_words[16]; const uint32_t *words = buffer; size_t nwords = len / sizeof (uint32_t); const uint32_t *endp = words + nwords; uint32_t A = ctx->A; uint32_t B = ctx->B; uint32_t C = ctx->C; uint32_t D = ctx->D; /* First increment the byte count. RFC 1321 specifies the possible length of the file up to 2^64 bits. Here we only compute the number of bytes. Do a double word increment. */ ctx->total[0] += len; if (ctx->total[0] < len) ++ctx->total[1]; /* Process all bytes in the buffer with 64 bytes in each round of the loop. */ while (words < endp) { uint32_t *cwp = correct_words; uint32_t A_save = A; uint32_t B_save = B; uint32_t C_save = C; uint32_t D_save = D; /* First round: using the given function, the context and a constant the next context is computed. Because the algorithms processing unit is a 32-bit word and it is determined to work on words in little endian byte order we perhaps have to change the byte order before the computation. To reduce the work for the next steps we store the swapped words in the array CORRECT_WORDS. */ #define OP(a, b, c, d, s, T) \ do \ { \ a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ ++words; \ CYCLIC (a, s); \ a += b; \ } \ while (0) /* It is unfortunate that C does not provide an operator for cyclic rotation. Hope the C compiler is smart enough. */ #define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) /* Before we start, one word to the strange constants. They are defined in RFC 1321 as T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 Here is an equivalent invocation using Perl: perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' */ /* Round 1. */ OP (A, B, C, D, 7, 0xd76aa478); OP (D, A, B, C, 12, 0xe8c7b756); OP (C, D, A, B, 17, 0x242070db); OP (B, C, D, A, 22, 0xc1bdceee); OP (A, B, C, D, 7, 0xf57c0faf); OP (D, A, B, C, 12, 0x4787c62a); OP (C, D, A, B, 17, 0xa8304613); OP (B, C, D, A, 22, 0xfd469501); OP (A, B, C, D, 7, 0x698098d8); OP (D, A, B, C, 12, 0x8b44f7af); OP (C, D, A, B, 17, 0xffff5bb1); OP (B, C, D, A, 22, 0x895cd7be); OP (A, B, C, D, 7, 0x6b901122); OP (D, A, B, C, 12, 0xfd987193); OP (C, D, A, B, 17, 0xa679438e); OP (B, C, D, A, 22, 0x49b40821); /* For the second to fourth round we have the possibly swapped words in CORRECT_WORDS. Redefine the macro to take an additional first argument specifying the function to use. */ #undef OP #define OP(f, a, b, c, d, k, s, T) \ do \ { \ a += f (b, c, d) + correct_words[k] + T; \ CYCLIC (a, s); \ a += b; \ } \ while (0) /* Round 2. */ OP (FG, A, B, C, D, 1, 5, 0xf61e2562); OP (FG, D, A, B, C, 6, 9, 0xc040b340); OP (FG, C, D, A, B, 11, 14, 0x265e5a51); OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); OP (FG, A, B, C, D, 5, 5, 0xd62f105d); OP (FG, D, A, B, C, 10, 9, 0x02441453); OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); OP (FG, D, A, B, C, 14, 9, 0xc33707d6); OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); OP (FG, B, C, D, A, 8, 20, 0x455a14ed); OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); OP (FG, C, D, A, B, 7, 14, 0x676f02d9); OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); /* Round 3. */ OP (FH, A, B, C, D, 5, 4, 0xfffa3942); OP (FH, D, A, B, C, 8, 11, 0x8771f681); OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); OP (FH, B, C, D, A, 14, 23, 0xfde5380c); OP (FH, A, B, C, D, 1, 4, 0xa4beea44); OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); OP (FH, B, C, D, A, 6, 23, 0x04881d05); OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); /* Round 4. */ OP (FI, A, B, C, D, 0, 6, 0xf4292244); OP (FI, D, A, B, C, 7, 10, 0x432aff97); OP (FI, C, D, A, B, 14, 15, 0xab9423a7); OP (FI, B, C, D, A, 5, 21, 0xfc93a039); OP (FI, A, B, C, D, 12, 6, 0x655b59c3); OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); OP (FI, C, D, A, B, 10, 15, 0xffeff47d); OP (FI, B, C, D, A, 1, 21, 0x85845dd1); OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); OP (FI, C, D, A, B, 6, 15, 0xa3014314); OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); OP (FI, A, B, C, D, 4, 6, 0xf7537e82); OP (FI, D, A, B, C, 11, 10, 0xbd3af235); OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); OP (FI, B, C, D, A, 9, 21, 0xeb86d391); /* Add the starting values of the context. */ A += A_save; B += B_save; C += C_save; D += D_save; } /* Put checksum in context given as argument. */ ctx->A = A; ctx->B = B; ctx->C = C; ctx->D = D; } cntlm-0.92.3/config.c0000664000175000017500000000675311664711303013312 0ustar daviddavid/* * These are very basic config file routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include "globals.h" #include "config.h" #include "utils.h" /* static const char *globals[] = { "Allow", "Deny", "Gateway", "Listen", "SOCKS5Proxy", "SOCKS5User", "NTLMToBasic", "Tunnel" }; */ config_t config_open(const char *fname) { config_t rc; FILE *fp; char *buf, *tmp, *key, *value; char section[MINIBUF_SIZE] = "global"; int i, j, slen, len, quote; //printf("sizeof = %d\n", sizeof(globals) / sizeof(char *)); fp = fopen(fname, "r"); if (!fp) return NULL; buf = new(BUFSIZE); rc = (config_t)new(sizeof(struct config_s)); rc->options = NULL; while (!feof(fp)) { quote = 0; tmp = fgets(buf, BUFSIZE, fp); if (!tmp) break; len = MIN(BUFSIZE, strlen(buf)); if (!len || feof(fp)) continue; /* * Find first non-empty character */ for (i = j = 0; j < len && isspace(buf[j]); ++j); /* * Comment? */ if (j >= len || buf[j] == '#' || buf[j] == ';') continue; /* * Find end of keyword */ for (i = j; j < len && isalnum(buf[j]); ++j); /* * Malformed? */ if (j >= len) continue; /* * Is it a section? */ if (buf[j] == '[') { for (++j; j < len && isspace(buf[j]); ++j); for (slen = j; j < len && j-slen < MINIBUF_SIZE-1 && buf[j] != ']' && !isspace(buf[j]); ++j); if (j-slen > 0) { strlcpy(section, buf+slen, j-slen+1); } continue; } /* * It's an OK keyword */ key = substr(buf, i, j-i); /* * Find next non-empty character */ for (i = j; j < len && isspace(buf[j]); ++j); if (j >= len || buf[j] == '#' || buf[j] == ';') continue; /* * Is value quoted? */ if (buf[j] == '"') { quote = 1; for (i = ++j; j < len && buf[i] != '"'; ++i); if (i >= len) continue; } else i = len; /* * Get value as quoted or until EOL/comment */ value = substr(buf, j, i-j); if (!quote) { i = strcspn(value, "#"); if (i != strlen(value)) value[i] = 0; trimr(value); } if (debug) printf("section: %s, %s = '%s'\n", section, key, value); rc->options = hlist_add(rc->options, key, value, HLIST_NOALLOC, HLIST_NOALLOC); } free(buf); fclose(fp); return rc; } void config_set(config_t cf, char *option, char *value) { cf->options = hlist_mod(cf->options, option, value, 1); } char *config_pop(config_t cf, const char *option) { char *tmp; tmp = hlist_get(cf->options, option); if (tmp) { tmp = strdup(tmp); cf->options = hlist_del(cf->options, option); } return tmp; } int config_count(config_t cf) { return hlist_count(cf->options); } void config_close(config_t cf) { if (cf == NULL) return; cf->options = hlist_free(cf->options); free(cf); } cntlm-0.92.3/utils.c0000664000175000017500000004462611664711303013206 0ustar daviddavid/* * These are helping routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include #include #include #include #include #include #include "config/config.h" #include "swap.h" #include "utils.h" #include "socket.h" char hextab[17] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 0}; int hexindex[128] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; void myexit(int rc) { if (rc) fprintf(stderr, "Exitting with error. Check daemon logs or run with -v.\n"); exit(rc); } void croak(const char *msg, int console) { if (console) printf("%s", msg); else syslog(LOG_ERR, "%s", msg); myexit(1); } /* * Add a new item to a list. Every plist_t variable must be * initialized to NULL (or pass NULL for "list" when adding * the first item). This is for simplicity's sake (we don't * need any plist_new). * * This list type allows to store an arbitrary pointer * associating it with the key. */ plist_t plist_add(plist_t list, unsigned long key, void *aux) { plist_t tmp, t = list; tmp = malloc(sizeof(struct plist_s)); tmp->key = key; tmp->aux = aux; tmp->next = NULL; if (list == NULL) return tmp; while (t->next) t = t->next; t->next = tmp; return list; } /* * Delete an item from the list, possibly returning NULL when * the list is empty or nothing was found. */ plist_t plist_del(plist_t list, unsigned long key) { plist_t ot = NULL, t = list; while (t) { if (t->key == key) break; ot = t; t = t->next; } if (t) { plist_t tmp = t->next; if (t->aux) free(t->aux); free(t); if (ot == NULL) return tmp; ot->next = tmp; } return list; } /* * Return true if an item is present in the list. */ int plist_in(plist_t list, unsigned long key) { plist_t t = list; while (t) { if (t->key == key) break; t = t->next; } return (t != NULL); } /* * For debugging purposes - dump the entire contents * of a list. */ void plist_dump(plist_t list) { plist_t t; t = list; while (t) { printf("List data: %lu => 0x%8p\n", (unsigned long int)t->key, t->aux); t = t->next; } } /* * Return the pointer associated with the key. */ char *plist_get(plist_t list, int key) { plist_t t = list; while (t) { if (t->key == key) break; t = t->next; } return (t == NULL ? NULL : t->aux); } /* * Scan the list for an open descriptor (socket), possibly * discarding all closed ones on the way. Return the first * match. * * Use this method only for lists of descriptors! * * In conjunction with plist_add, the list behaves as a FIFO. * This feature is used for rotating cached connections in the * list, so that none is left too long unused (proxy timeout). * * Returns key value (descriptor) and if aux != NULL, *aux gets * aux pointer value (which caller must free if != NULL). */ int plist_pop(plist_t *list, void **aux) { plist_t tmp, t; int id = 0; int ok = 0; void *a = NULL; if (list == NULL || *list == NULL) return 0; t = *list; while (!ok && t) { id = t->key; a = t->aux; tmp = t->next; if (so_closed(id)) { close(id); if (t->aux) free(t->aux); } else ok = 1; free(t); t = tmp; } *list = t; if (ok) { if (aux != NULL) *aux = a; return id; } return 0; } /* * Return the number of items in a list. */ int plist_count(plist_t list) { plist_t t = list; int rc = 0; while (t) { rc++; t = t->next; } return rc; } /* * Free the list. */ plist_t plist_free(plist_t list) { plist_t t = list; while (list) { t = list->next; if (list->aux) free(list->aux); free(list); list = t; } return NULL; } /* * The same as plist_add. Here we have two other arguments. * They are boolean flags - HLIST_ALLOC means to duplicate a * key/value, HLIST_NOALLOC means to store the pointer directly. * * Caller decides this on a by-call basis. Part of the manipulation * routines is a "free". That method always deallocates both the * key and the value. So for static or temporary keys/values, * the caller can instruct us to duplicate the necessary amount * of heap. This mechanism is used to minimize memory-related * bugs throughout the code and tons of free's. */ hlist_t hlist_add(hlist_t list, char *key, char *value, hlist_add_t allockey, hlist_add_t allocvalue) { hlist_t tmp, t = list; if (key == NULL || value == NULL) return list; tmp = malloc(sizeof(struct hlist_s)); tmp->key = (allockey == HLIST_ALLOC ? strdup(key) : key); tmp->value = (allocvalue == HLIST_ALLOC ? strdup(value) : value); tmp->next = NULL; tmp->islist = 0; if (list == NULL) return tmp; while (t->next) t = t->next; t->next = tmp; return list; } /* * Return a duplicate of the list (copy). */ hlist_t hlist_dup(hlist_t list) { hlist_t tmp = NULL, t = list; while (t) { tmp = hlist_add(tmp, t->key, t->value, HLIST_ALLOC, HLIST_ALLOC); t = t->next; } return tmp; } /* * Remove an item from the list. */ hlist_t hlist_del(hlist_t list, const char *key) { hlist_t ot = NULL, t = list; while (t) { if (!strcasecmp(t->key, key)) break; ot = t; t = t->next; } if (t) { hlist_t tmp = t->next; free(t->key); free(t->value); free(t); if (ot == NULL) return tmp; ot->next = tmp; } return list; } /* * Change the value of a key. If add is true, we store it in the * list if the key is not found. Unlike hlist_add, which offers * pointer storage or memory duplication for both the key and the * value separately, hlist_mod always duplicates. * * Used to add a header, which might already be present. */ hlist_t hlist_mod(hlist_t list, char *key, char *value, int add) { hlist_t t = list; while (t) { if (!strcasecmp(t->key, key)) break; t = t->next; } if (t) { free(t->value); t->value = strdup(value); } else if (add) { list = hlist_add(list, key, value, HLIST_ALLOC, HLIST_ALLOC); } return list; } /* * Return true is the key is in the list. */ int hlist_in(hlist_t list, const char *key) { hlist_t t = list; while (t) { if (!strcasecmp(t->key, key)) break; t = t->next; } return (t != NULL); } /* * Return the number of items in a list. */ int hlist_count(hlist_t list) { hlist_t t = list; int rc = 0; while (t) { rc++; t = t->next; } return rc; } /* * Return the value for the key. */ char *hlist_get(hlist_t list, const char *key) { hlist_t t = list; while (t) { if (!strcasecmp(t->key, key)) break; t = t->next; } return (t == NULL ? NULL : t->value); } /* * Test if substr is part of the header's value. * Both case-insensitive. */ int hlist_subcmp(hlist_t list, const char *key, const char *substr) { int found = 0; char *tmp, *low; lowercase(low = strdup(substr)); tmp = hlist_get(list, key); if (tmp) { lowercase(tmp = strdup(tmp)); if (strstr(tmp, low)) found = 1; free(tmp); } free(low); return found; } /* * Test if substr is part of the header's value. * Both case-insensitive, checks all headers, not just first one. */ int hlist_subcmp_all(hlist_t list, const char *key, const char *substr) { hlist_t t = list; int found = 0; char *tmp, *low; lowercase(low = strdup(substr)); while (t) { if (!strcasecmp(t->key, key)) { lowercase(tmp = strdup(t->value)); if (strstr(tmp, low)) found = 1; free(tmp); } t = t->next; } free(low); return found; } /* * Free the list. For more about list memory management, * se hlist_add. */ hlist_t hlist_free(hlist_t list) { hlist_t t = list; while (list) { t = list->next; free(list->key); free(list->value); free(list); list = t; } return NULL; } /* * This is for debugging purposes. */ void hlist_dump(hlist_t list) { hlist_t t; t = list; while (t) { printf("%-30s => %s\n", t->key, t->value); t = t->next; } } /* * Standard substr. To prevent modification of the source * (terminating \x0), return the result in a new memory. */ char *substr(const char *src, int pos, int len) { int l; char *tmp; if (len == 0) len = strlen(src); l = MIN(len, strlen(src)-pos); if (l <= 0) return new(1); tmp = new(l+1); strlcpy(tmp, src+pos, l+1); return tmp; } /* * Allocate memory and initialize a new rr_data_t structure. */ rr_data_t new_rr_data(void) { rr_data_t data; data = malloc(sizeof(struct rr_data_s)); data->req = 0; data->code = 0; data->skip_http = 0; data->body_len = 0; data->empty = 1; data->port = 0; data->headers = NULL; data->method = NULL; data->url = NULL; data->rel_url = NULL; data->hostname = NULL; data->http = NULL; data->msg = NULL; data->body = NULL; data->errmsg = NULL; /* for static strings - we don't free, dup, nor copy */ return data; } /* * Copy the req/res data. */ rr_data_t copy_rr_data(rr_data_t dst, rr_data_t src) { if (src == NULL || dst == NULL) return NULL; reset_rr_data(dst); dst->req = src->req; dst->code = src->code; dst->skip_http = src->skip_http; dst->body_len = src->body_len; dst->empty = src->empty; dst->port = src->port; if (src->headers) dst->headers = hlist_dup(src->headers); if (src->method) dst->method = strdup(src->method); if (src->url) dst->url = strdup(src->url); if (src->rel_url) dst->rel_url = strdup(src->rel_url); if (src->hostname) dst->hostname = strdup(src->hostname); if (src->http) dst->http = strdup(src->http); if (src->msg) dst->msg = strdup(src->msg); if (src->body && src->body_len > 0) { dst->body = new(src->body_len); memcpy(dst->body, src->body, src->body_len); } return dst; } /* * Duplicate the req/res data. */ rr_data_t dup_rr_data(rr_data_t data) { rr_data_t tmp; if (data == NULL) return NULL; tmp = new_rr_data(); return copy_rr_data(tmp, data); } /* * Reset, freeing if neccessary */ rr_data_t reset_rr_data(rr_data_t data) { if (data == NULL) return NULL; data->req = 0; data->code = 0; data->skip_http = 0; data->body_len = 0; data->empty = 1; data->port = 0; if (data->headers) hlist_free(data->headers); if (data->method) free(data->method); if (data->url) free(data->url); if (data->rel_url) free(data->rel_url); if (data->hostname) free(data->hostname); if (data->http) free(data->http); if (data->msg) free(data->msg); if (data->body) free(data->body); data->headers = NULL; data->method = NULL; data->url = NULL; data->rel_url = NULL; data->hostname = NULL; data->http = NULL; data->msg = NULL; data->body = NULL; data->errmsg = NULL; return data; } /* * Free rr_data_t structure. We also take care of freeing * the memory of its members. */ void free_rr_data(rr_data_t data) { if (data == NULL) return; if (data->headers) hlist_free(data->headers); if (data->method) free(data->method); if (data->url) free(data->url); if (data->rel_url) free(data->rel_url); if (data->hostname) free(data->hostname); if (data->http) free(data->http); if (data->msg) free(data->msg); if (data->body) free(data->body); free(data); } /* * Cut the whitespace at the end of a string. */ char *trimr(char *buf) { int i; for (i = strlen(buf)-1; i >= 0 && isspace(buf[i]); --i); buf[i+1] = 0; return buf; } #if config_strdup == 0 /* * Our implementation of non-POSIX strdup() */ char *strdup(const char *src) { size_t len; char *tmp; if (!src) return NULL; len = strlen(src)+1; tmp = calloc(1, len); memcpy(tmp, src, len-1); return tmp; } #endif /* * More intuitive version of strncpy with string termination * from OpenBSD */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++); } return (s - src - 1); /* count does not include NUL */ } /* * More intuitive version os strncat with string termination * from OpenBSD */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return (dlen + (s - src)); /* count does not include NUL */ } /* * Shortcut for malloc/memset zero. */ char *new(size_t size) { char *tmp; tmp = malloc(size); memset(tmp, 0, size); return tmp; } /* * Self-explanatory. */ char *lowercase(char *str) { int i; for (i = 0; i < strlen(str); ++i) str[i] = tolower(str[i]); return str; } /* * Self-explanatory. */ char *uppercase(char *str) { int i; for (i = 0; i < strlen(str); ++i) str[i] = toupper(str[i]); return str; } int unicode(char **dst, char *src) { char *tmp; int l, i; if (!src) { *dst = NULL; return 0; } l = MIN(64, strlen(src)); tmp = new(2*l); for (i = 0; i < l; ++i) tmp[2*i] = src[i]; *dst = tmp; return 2*l; } char *urlencode(const char *str) { char *tmp; int i, pos; tmp = new(strlen(str)*3 + 1); for (pos = 0, i = 0; i < strlen(str); ++i) { if (isdigit(str[i]) || (tolower(str[i]) >= 'a' && tolower(str[i]) <= 'z') || str[i] == '.' || str[i] == '-' || str[i] == '_' || str[i] == '~') { tmp[pos++] = str[i]; } else { sprintf(tmp+pos, "%%%X", (unsigned char)str[i]); pos += 3; } } return tmp; } char *printmem(char *src, size_t len, int bitwidth) { char *tmp; int i; tmp = new(2*len+1); for (i = 0; i < len; ++i) { tmp[i*2] = hextab[((uint8_t)src[i] ^ (uint8_t)(7-bitwidth)) >> 4]; tmp[i*2+1] = hextab[(src[i] ^ (uint8_t)(7-bitwidth)) & 0x0F]; } return tmp; } char *scanmem(char *src, int bitwidth) { int h, l, i, bytes; char *tmp; if (strlen(src) % 2) return NULL; bytes = strlen(src)/2; tmp = new(bytes+1); for (i = 0; i < bytes; ++i) { h = hexindex[(int)src[i*2]]; l = hexindex[(int)src[i*2+1]]; if (h < 0 || l < 0) { free(tmp); return NULL; } tmp[i] = ((h << 4) + l) ^ (uint8_t)(7-bitwidth); } tmp[i] = 0; return tmp; } /* * BASE64 CODE FROM MUTT BEGIN - ORIGINAL COPYRIGHT APPLIES: * * Copyright (C) 1996-2001 Michael R. Elkins * Copyright (C) 1996-2001 Brandon Long * Copyright (C) 1997-2001 Thomas Roessler * Copyright (C) 1998-2001 Werner Koch * Copyright (C) 1999-2001 Brendan Cully * Copyright (C) 1999-2001 Tommi Komulainen * Copyright (C) 2000-2001 Edmund Grimley Evans * */ #define BAD -1 #define base64val(c) index64[(unsigned int)(c)] char base64[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/' }; int index64[128] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60, 61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13, 14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26, 27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45, 46,47,48,49,50,51,-1,-1,-1,-1,-1 }; void to_base64(unsigned char *out, const unsigned char *in, size_t len, size_t olen) { while (len >= 3 && olen > 10) { *out++ = base64[in[0] >> 2]; *out++ = base64[((in[0] << 4) & 0x30) | (in[1] >> 4)]; *out++ = base64[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; *out++ = base64[in[2] & 0x3f]; olen -= 4; len -= 3; in += 3; } /* clean up remainder */ if (len > 0 && olen > 4) { unsigned char fragment; *out++ = base64[in[0] >> 2]; fragment = (in[0] << 4) & 0x30; if (len > 1) fragment |= in[1] >> 4; *out++ = base64[fragment]; *out++ = (len < 2) ? '=' : base64[(in[1] << 2) & 0x3c]; *out++ = '='; } *out = '\0'; } /* Convert '\0'-terminated base 64 string to raw bytes. * Returns length of returned buffer, or -1 on error */ int from_base64(char *out, const char *in) { int len = 0; register unsigned char digit1, digit2, digit3, digit4; do { digit1 = in[0]; if (digit1 > 127 || base64val (digit1) == BAD) return -1; digit2 = in[1]; if (digit2 > 127 || base64val (digit2) == BAD) return -1; digit3 = in[2]; if (digit3 > 127 || ((digit3 != '=') && (base64val (digit3) == BAD))) return -1; digit4 = in[3]; if (digit4 > 127 || ((digit4 != '=') && (base64val (digit4) == BAD))) return -1; in += 4; /* digits are already sanity-checked */ *out++ = (base64val(digit1) << 2) | (base64val(digit2) >> 4); len++; if (digit3 != '=') { *out++ = ((base64val(digit2) << 4) & 0xf0) | (base64val(digit3) >> 2); len++; if (digit4 != '=') { *out++ = ((base64val(digit3) << 6) & 0xc0) | base64val(digit4); len++; } } } while (*in && digit4 != '='); return len; } /* * CODE FROM MUTT END */ cntlm-0.92.3/acl.h0000664000175000017500000000222010642235400012564 0ustar daviddavid/* * These are ACL routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _ACL_H #define _ACL_H #include #include "utils.h" /* * ACL rule datatypes. */ enum acl_t { ACL_ALLOW = 0, ACL_DENY }; typedef struct { unsigned int ip; int mask; } network_t; extern int acl_add(plist_t *rules, char *spec, enum acl_t acl); extern enum acl_t acl_check(plist_t rules, struct in_addr naddr); #endif /* _ACL_H */ cntlm-0.92.3/Makefile.xlc0000664000175000017500000000315111351243516014112 0ustar daviddavid# # You can tweak these three variables to make things install where you # like, but do not touch more unless you know what you are doing. ;) # SYSCONFDIR=/usr/local/etc BINDIR=/usr/local/bin MANDIR=/usr/local/man # # CC=xlc_r OBJS=utils.o ntlm.o xcrypt.o config.o socket.o acl.o auth.o http.o forward.o direct.o scanner.o pages.o main.o CFLAGS=$(FLAGS) -O3 -D_POSIX_C_SOURCE=200112 -D_ISOC99_SOURCE -D_REENTRANT -DVERSION=\"`cat VERSION`\" LDFLAGS=-lpthread NAME=cntlm $(NAME): $(OBJS) $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) main.o: main.c if [ -z "$(SYSCONFDIR)" ]; then \ $(CC) $(CFLAGS) -c main.c -o $@; \ else \ $(CC) $(CFLAGS) -DSYSCONFDIR=\"$(SYSCONFDIR)\" -c main.c -o $@; \ fi install: $(NAME) if [ -f /usr/bin/oslevel ]; then \ install -M 0755 -S -f $(BINDIR) $(NAME); \ install -M 0644 -f $(MANDIR)/man1 doc/$(NAME).1; \ install -M 0600 -c $(SYSCONFDIR) doc/$(NAME).conf; \ else \ install -D -m 0755 -s $(NAME) $(BINDIR)/$(NAME); \ install -D -m 0644 doc/$(NAME).1 $(MANDIR)/man1/$(NAME).1; \ [ -f $(SYSCONFDIR)/$(NAME).conf -o -z "$(SYSCONFDIR)" ] \ || install -D -m 0600 doc/$(NAME).conf $(SYSCONFDIR)/$(NAME).conf; \ fi uninstall: rm -f $(BINDIR)/$(NAME) $(MANDIR)/man1/$(NAME).1 2>/dev/null || true clean: @rm -f *.o cntlm cntlm.exe configure-stamp build-stamp config/config.h 2>/dev/null @rm -f cntlm-install win/cyg* win/cntlm* 2>/dev/null @rm -f config/endian config/gethostname config/strdup config/socklen_t config/*.exe @if [ -h Makefile ]; then rm -f Makefile; mv Makefile.gcc Makefile; fi distclean: clean @rm -f *.deb *.rpm *.tgz *.tar.gz *.tar.bz2 tags ctags pid 2>/dev/null cntlm-0.92.3/VERSION0000664000175000017500000000000711725656744012752 0ustar daviddavid0.92.3 cntlm-0.92.3/pages.h0000664000175000017500000000201311351425702013130 0ustar daviddavid/* * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include "utils.h" #include "string.h" #include "stdio.h" extern char *gen_407_page(const char *http); extern char *gen_401_page(const char *http, const char *host, int port); extern char *gen_denied_page(const char *ip); extern char *gen_502_page(const char *http, const char *msg); cntlm-0.92.3/globals.h0000664000175000017500000000334011665255101013462 0ustar daviddavid/* * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ /* * These are globals, mostly run-time options, defined and setup in main module * proxy.c */ #ifndef _GLOBALS_H #define _GLOBALS_H #include #include "utils.h" #include "auth.h" extern int debug; extern struct auth_s *g_creds; /* global NTLM credentials */ extern int ntlmbasic; /* forward_request() */ extern int serialize; extern int scanner_plugin; extern long scanner_plugin_maxsize; extern plist_t threads_list; extern pthread_mutex_t threads_mtx; extern plist_t connection_list; extern pthread_mutex_t connection_mtx; extern int parent_count; extern plist_t parent_list; /* * just malloc/free sizeof(proxy_t) */ typedef struct { char hostname[64]; struct auth_s creds; struct in_addr host; int port; int resolved; } proxy_t; extern hlist_t header_list; /* forward_request() */ extern hlist_t users_list; /* socks5_thread() */ extern plist_t scanner_agent_list; /* scanner_hook() */ extern plist_t noproxy_list; /* proxy_thread() */ #endif /* _GLOBALS_H */ cntlm-0.92.3/auth.c0000664000175000017500000000617711352535506013011 0ustar daviddavid/* * Credentials related structures and routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include "utils.h" #include "auth.h" struct auth_s *new_auth(void) { struct auth_s *tmp; tmp = (struct auth_s *)malloc(sizeof(struct auth_s)); if (tmp == NULL) return NULL; memset(tmp->user, 0, MINIBUF_SIZE); memset(tmp->domain, 0, MINIBUF_SIZE); memset(tmp->workstation, 0, MINIBUF_SIZE); memset(tmp->passntlm2, 0, MINIBUF_SIZE); memset(tmp->passnt, 0, MINIBUF_SIZE); memset(tmp->passlm, 0, MINIBUF_SIZE); tmp->hashntlm2 = 1; tmp->hashnt = 0; tmp->hashlm = 0; tmp->flags = 0; return tmp; } struct auth_s *copy_auth(struct auth_s *dst, struct auth_s *src, int fullcopy) { dst->hashntlm2 = src->hashntlm2; dst->hashnt = src->hashnt; dst->hashlm = src->hashlm; dst->flags = src->flags; strlcpy(dst->domain, src->domain, MINIBUF_SIZE); strlcpy(dst->workstation, src->workstation, MINIBUF_SIZE); if (fullcopy) { strlcpy(dst->user, src->user, MINIBUF_SIZE); if (src->passntlm2) memcpy(dst->passntlm2, src->passntlm2, MINIBUF_SIZE); if (src->passnt) memcpy(dst->passnt, src->passnt, MINIBUF_SIZE); if (src->passlm) memcpy(dst->passlm, src->passlm, MINIBUF_SIZE); } else { memset(dst->user, 0, MINIBUF_SIZE); memset(dst->passntlm2, 0, MINIBUF_SIZE); memset(dst->passnt, 0, MINIBUF_SIZE); memset(dst->passlm, 0, MINIBUF_SIZE); } return dst; } struct auth_s *dup_auth(struct auth_s *creds, int fullcopy) { struct auth_s *tmp; tmp = new_auth(); if (tmp == NULL) return NULL; return copy_auth(tmp, creds, fullcopy); } void dump_auth(struct auth_s *creds) { char *tmp; printf("Credentials structure dump:\n"); if (creds == NULL) { printf("Struct is not allocated!\n"); return; } printf("User: %s\n", creds->user); printf("Domain: %s\n", creds->domain); printf("Wks: %s\n", creds->workstation); printf("HashNTLMv2: %d\n", creds->hashntlm2); printf("HashNT: %d\n", creds->hashnt); printf("HashLM: %d\n", creds->hashlm); printf("Flags: %X\n", creds->flags); if (creds->passntlm2) { tmp = printmem(creds->passntlm2, 16, 8); printf("PassNTLMv2: %s\n", tmp); free(tmp); } if (creds->passnt) { tmp = printmem(creds->passnt, 16, 8); printf("PassNT: %s\n", tmp); free(tmp); } if (creds->passlm) { tmp = printmem(creds->passlm, 16, 8); printf("PassLM: %s\n\n", tmp); free(tmp); } } cntlm-0.92.3/config.h0000664000175000017500000000304210712145372013303 0ustar daviddavid/* * These are very basic config file routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _CONFIG_H #define _CONFIG_H #include #include "utils.h" #define CFG_OPTION(cf, opt, var, size) { char *__tmp = NULL; if ((__tmp=config_pop(cf, opt))) { strlcpy(var, __tmp, size); } if (__tmp) free(__tmp); } #define CFG_DEFAULT(cf, opt, var, size) { char *__tmp = NULL; if ((__tmp=config_pop(cf, opt)) && !strlen(var)) { strlcpy(var, __tmp, size); } if (__tmp) free(__tmp); } typedef struct config_s *config_t; struct config_s { hlist_t options; }; extern config_t config_open(const char *fname); extern void config_set(config_t cf, char *option, char *value); extern char *config_pop(config_t cf, const char *option); extern int config_count(config_t cf); extern void config_close(config_t cf); #endif /* _CONFIG_H */ cntlm-0.92.3/swap.h0000664000175000017500000000424211353766473013030 0ustar daviddavid/* * These are little/big endian routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _SWAP_H #define _SWAP_H #include #include "config/config.h" #define swap16(x) \ ((uint16_t)( \ (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ (((uint16_t)(x) & (uint16_t)0xff00U) >> 8) )) #define swap32(x) \ ((uint32_t)( \ (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24) )) #define swap64(x) \ ((uint64_t)( \ (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56) | \ (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) )) #if config_endian == 0 # define U16LE(x) swap16(x) # define U32LE(x) swap32(x) # define U64LE(x) swap64(x) # define U16BE(x) (x) # define U32BE(x) (x) # define U64BE(x) (x) #else # define U16LE(x) (x) # define U32LE(x) (x) # define U64LE(x) (x) # define U16BE(x) swap16(x) # define U32BE(x) swap32(x) # define U64BE(x) swap64(x) #endif #endif /* _SWAP_H */ cntlm-0.92.3/auth.h0000664000175000017500000000372211352535506013007 0ustar daviddavid/* * Credentials related structures and routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _AUTH_H #define _AUTH_H #include #include "utils.h" /* * Although I always prefer structs with pointer refs, I need direct storage * here to be able to alloc/free it in one go. It is used in a plist_t which * frees its items, but not recursively. */ struct auth_s { char user[MINIBUF_SIZE]; char domain[MINIBUF_SIZE]; char workstation[MINIBUF_SIZE]; char passlm[MINIBUF_SIZE]; char passnt[MINIBUF_SIZE]; char passntlm2[MINIBUF_SIZE]; int hashntlm2; int hashnt; int hashlm; uint32_t flags; }; #define auth_strcpy(creds, var, value) \ if ((creds) && (value)) { \ strlcpy(((creds)->var), (value), MINIBUF_SIZE); \ } #define auth_memcpy(creds, var, value, len) \ if ((creds) && (value)) { \ memcpy(((creds)->var), (value), MIN(len, MINIBUF_SIZE)); \ } /* * No free_auth() required, just use free() * new_auth() is also just a convenience malloc/memset() wrapper */ extern struct auth_s *new_auth(void); extern struct auth_s *copy_auth(struct auth_s *dst, struct auth_s *src, int fullcopy); extern struct auth_s *dup_auth(struct auth_s *creds, int fullcopy); extern void dump_auth(struct auth_s *creds); #endif /* _AUTH_H */ cntlm-0.92.3/scanner.c0000664000175000017500000001743611670713123013475 0ustar daviddavid/* * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "socket.h" #include "http.h" #include "globals.h" #include "forward.h" #include "scanner.h" /* * This code is a piece of shit, but it works. Cannot rewrite it now, because * I don't have ISA AV filter anymore - wouldn't be able to test it. */ int scanner_hook(rr_data_t request, rr_data_t response, struct auth_s *credentials, int cd, int *sd, long maxKBs) { char *buf, *line, *pos, *tmp, *pat, *post, *isaid, *uurl; int bsize, lsize, size, len, i, w, nc; rr_data_t newreq, newres; plist_t list; int ok = 1; int done = 0; int headers_initiated = 0; long c, progress = 0, filesize = 0; /* * Let's limit the responses we examine to an absolute minimum */ if (!request->req || response->code != 200 || http_has_body(request, response) != -1 || hlist_subcmp(response->headers, "Transfer-Encoding", "chunked") || !hlist_subcmp(response->headers, "Proxy-Connection", "close")) return PLUG_SENDHEAD | PLUG_SENDDATA; tmp = hlist_get(request->headers, "User-Agent"); if (tmp) { tmp = lowercase(strdup(tmp)); list = scanner_agent_list; while (list) { pat = lowercase(strdup(list->aux)); if (debug) printf("scanner_hook: matching U-A header (%s) to %s\n", tmp, pat); if (!fnmatch(pat, tmp, 0)) { if (debug) printf("scanner_hook: positive match!\n"); maxKBs = 0; free(pat); break; } free(pat); list = list->next; } free(tmp); } bsize = SAMPLE; buf = new(bsize); len = 0; do { size = read(*sd, buf + len, SAMPLE - len - 1); if (debug) printf("scanner_hook: read %d of %d\n", size, SAMPLE - len); if (size > 0) len += size; } while (size > 0 && len < SAMPLE - 1); if (strstr(buf, "Downloading status") && (pos=strstr(buf, "ISAServerUniqueID=")) && (pos = strchr(pos, '"'))) { pos++; c = strlen(pos); for (i = 0; i < c && pos[i] != '"'; ++i); if (pos[i] == '"') { isaid = substr(pos, 0, i); if (debug) printf("scanner_hook: ISA id = %s\n", isaid); lsize = BUFSIZE; line = new(lsize); do { i = so_recvln(*sd, &line, &lsize); c = strlen(line); if (len + c >= bsize) { bsize *= 2; tmp = realloc(buf, bsize); if (tmp == NULL) break; else buf = tmp; } strcat(buf, line); len += c; if (i >= 0 && ( ((pos = strstr(line, "UpdatePage(")) && isdigit(pos[11])) || ((pos = strstr(line, "DownloadFinished(")) && isdigit(pos[17]) && (done = 1)) )) { if (debug) printf("scanner_hook: %s", line); if ((pos = strstr(line, "To be downloaded"))) { filesize = atol(pos+16); if (debug) { if (filesize > 0) { printf("scanner_hook: file size detected: %ld KiBs (max: %ld)\n", filesize/1024, maxKBs); } else { printf("scanner_hook: file size unknown -- quitting\n"); break; } } if (maxKBs && (maxKBs == 1 || filesize/1024 > maxKBs)) break; /* * We have to send HTTP protocol ID so we can send the notification * headers during downloading. Once we've done that, it cannot appear * again, which it would if we returned PLUG_SENDHEAD, so we must * remember to not include it. */ headers_initiated = 1; tmp = new(MINIBUF_SIZE); snprintf(tmp, MINIBUF_SIZE, "%s 200 OK\r\n", request->http); w = write(cd, tmp, strlen(tmp)); free(tmp); } if (!headers_initiated) { if (debug) printf("scanner_hook: Giving up, \"To be downloaded\" line not found!\n"); break; } /* * Send a notification header to the client, just so it doesn't timeout */ if (!done) { tmp = new(MINIBUF_SIZE); progress = atol(line+12); snprintf(tmp, MINIBUF_SIZE, "ISA-Scanner: %ld of %ld\r\n", progress, filesize); w = write(cd, tmp, strlen(tmp)); free(tmp); } /* * If download size is unknown beforehand, stop when downloaded amount is over ISAScannerSize */ if (!filesize && maxKBs && maxKBs != 1 && progress/1024 > maxKBs) break; } } while (i > 0 && !done); if (i >= 0 && done && (pos = strstr(line, "\",\"")+3) && (c = strchr(pos, '"') - pos) > 0) { tmp = substr(pos, 0, c); pos = urlencode(tmp); free(tmp); uurl = urlencode(request->url); post = new(BUFSIZE); snprintf(post, BUFSIZE-1, "%surl=%s&%sSaveToDisk=YES&%sOrig=%s", isaid, pos, isaid, isaid, uurl); if (debug) printf("scanner_hook: Getting file with URL data = %s\n", request->url); tmp = new(MINIBUF_SIZE); snprintf(tmp, MINIBUF_SIZE, "%d", (int)strlen(post)); newres = new_rr_data(); newreq = dup_rr_data(request); free(newreq->method); newreq->method = strdup("POST"); hlist_mod(newreq->headers, "Referer", request->url, 1); hlist_mod(newreq->headers, "Content-Type", "application/x-www-form-urlencoded", 1); hlist_mod(newreq->headers, "Content-Length", tmp, 1); free(tmp); nc = proxy_connect(credentials); c = proxy_authenticate(&nc, newreq, newres, credentials); if (c && newres->code == 407) { if (debug) printf("scanner_hook: Authentication OK, getting the file...\n"); } else { if (debug) printf("scanner_hook: Authentication failed or refused!\n"); close(nc); nc = 0; } /* * The POST request for the real file */ reset_rr_data(newres); if (nc && headers_send(nc, newreq) && write(nc, post, strlen(post)) && headers_recv(nc, newres)) { if (debug) hlist_dump(newres->headers); /* * We always know the filesize here. Send it to the client, because ISA doesn't!!! * The clients progress bar doesn't work without it and it stinks! */ if (filesize || progress) { tmp = new(20); snprintf(tmp, 20, "%ld", filesize ? filesize : progress); newres->headers = hlist_mod(newres->headers, "Content-Length", tmp, 1); } /* * Here we remember if previous code already sent some headers * to the client. In such case, do not include the HTTP/1.x ID. */ newres->skip_http = headers_initiated; copy_rr_data(response, newres); close(*sd); *sd = nc; len = 0; ok = PLUG_SENDHEAD | PLUG_SENDDATA; } else if (debug) printf("scanner_hook: New request failed\n"); free(newreq); free(newres); free(post); free(uurl); } free(line); free(isaid); } else if (debug) printf("scanner_hook: ISA id not found\n"); } if (len) { if (debug) { printf("scanner_hook: flushing %d original bytes\n", len); hlist_dump(response->headers); } if (!headers_send(cd, response)) { if (debug) printf("scanner_hook: failed to send headers\n"); free(buf); return PLUG_ERROR; } size = write(cd, buf, len); if (size > 0) ok = PLUG_SENDDATA; else ok = PLUG_ERROR; } if (debug) printf("scanner_hook: ending with %d\n", ok); free(buf); return ok; } cntlm-0.92.3/README0000664000175000017500000001214411363625217012554 0ustar daviddavidInstallation using packages ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Most of the popular distros contain cntlm packages.n their repositories. You can use the procedures described below to prepare a package of current cntlm version if desired. NOTE: generating packages traditionally requires root privileges (to be able to set proper ownership and permissions on package members). You can overcome that using fakeroot. However, to install your packages you have to be root. *** SOURCE TARBALL *** $ make tgz or $ make tbz2 *** DEBIAN PACKAGES *** 1) Quick way: $ make deb 2) From Debian/Ubuntu repository: Get these files (e.g. apt-get source cntlm): cntlm_0.XX-X.diff.gz cntlm_0.XX-X.dsc cntlm_0.XX.orig.tar.gz Compile: $ dpkg-source -x cntlm_0.XX-Y.dsc $ cd cntlm-0.XX/ $ dpkg-buildpackage -b -rfakeroot Upon installation, the package takes care of creating a dedicated user for cntlm, init script integration, manages eventual configuration file updates with new upstream versions, things like restart of the daemon after future updates, etc. You can later revert all these changes with one command, should you decide to remove cntlm from your system. *** RPM FROM SCRATCH *** 1) Quick way: $ make rpm # you'll need root privs. or fakeroot utility 2) Detailed howto (or if make rpm doesn't work for you) To build an RPM package from scratch, as root change to /usr/src/[redhat|rpm|whatever]/SOURCES Copy there all files from cntlm's rpm/ directory plus appropriate version of the source tar.bz2 (see SOURCE TARBALL section above) and type: $ rpmbuild -ba cntlm.spec Shortly after, you'll have source and binary RPMs ready in your ../SRPMS, resp. ../RPMS directories. If your build cannot find the default config file in /etc, you probably have broken RPM build environment. You should add this to your ~/.rpmmacros: %_sysconfdir /etc *** RPM FROM *.src.rpm *** If you just want to create a binary package from src.rpm, as root type: $ rpmbuild --rebuild pkgname.src.rpm Resulting binary RPM will be at /usr/src/..../RPMS If your build cannot find the default config file in /etc, you probably have broken RPM build environment. You should add this to your ~/.rpmmacros: %_sysconfdir /etc *** WINDOWS INSTALLER *** Traditional compilation steps: $ ./configure $ make Prepare all binaries, manuals, config templates, Start Menu links and InnoSetup project definition file: $ make win Then run InnoSetup compiler to pack it all into an automatic installer EXE: $ /your/path/to/ISCC.exe win/setup.iss or Open folder "win" in explorer, right click "setup.iss" and select "Compile". Both with generate an installer in the "win" folder. Traditional installation ~~~~~~~~~~~~~~~~~~~~~~~~ First, you have to compile cntlm. Using the Makefile, this should be very easy: $ ./configure $ make $ make install Cntlm does not require any dynamic libraries and there are no dependencies you have to satisfy before compilation, except for libpthreads. This library is required for all threaded applications and is very likely to be part of your system already, because it comes with libc. Next, install cntlm onto your system like so: Default installation directories are /usr/sbin, /usr/share/man and /etc. Should you want to install cntlm into a different location, change the DESTDIR installation prefix (from "/") to add a different installation prefix (e.g. /usr/local). To change individual directories, use BINDIR, MANDIR and SYSCONFDIR: $ make SYSCONFDIR=/etc BINDIR=/usr/bin MANDIR=/usr/share/man $ make install SYSCONFDIR=/etc BINDIR=/usr/bin MANDIR=/usr/share/man Cntlm is compiled with system-wide configuration file by default. That means whenever you run cntlm, it looks into a hardcoded path (SYSCONFDIR) and tries to load cntml.conf. You cannot make it not to do so, unless you use -c with an alternative file or /dev/null. This is standard behaviour and probably what you want. On the other hand, some of you might not want to use cntlm as a daemon started by init scripts and you would prefer setting up everything on the command line. This is possible, just comment out SYSCONFDIR variable definition in the Makefile before you compile cntlm and it will remove this feature. Installation includes the main binary, the man page (see "man cntlm") and if the default config feature was not removed, it also installs a configuration template. Please note that unlike bin and man targets, existing configuration is never overwritten during installation. In the doc/ directory you can find among other things a file called "cntlmd". It can be used as an init.d script. Architectures ~~~~~~~~~~~~~ The build system now has an autodetection of the build arch endianness. Every common CPU and OS out there is supported, including Windows, MacOS X, Linux, *BSD, AIX. Compilers ~~~~~~~~~ Cntlm is tested against GCC and IBM XL C/C++, other C compilers will work for you too. There are no compiler specific directives and options AFAIK. compilers might work for you (then again, they might not). Specific Makefiles for different compilers are supported by the ./configure script (e.g. Makefile.xlc) Contact ~~~~~~~ David Kubicek cntlm-0.92.3/xcrypt.h0000664000175000017500000001001710664307376013401 0ustar daviddavid/* des.c --- DES and Triple-DES encryption/decryption Algorithm * Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * Free Software Foundation, Inc. * * This file 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, or (at your * option) any later version. * * This file 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 file; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * ---------------------------------------------------------------------- * Functions to compute MD4 message digest of files or memory blocks. * according to the definition of MD4 in RFC 1320 from April 1992. Copyright * (C) 1995,1996,1997,1999,2000,2001,2002,2003,2005,2006 Free Software * Foundation, Inc. * * 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _XCRYPT_H #define _XCRYPT_H #include #include #include #include #define MD5_DIGEST_SIZE 16 #define MD5_BLOCK_SIZE 64 #define IPAD 0x36 #define OPAD 0x5c #define gl_des_ecb_encrypt(ctx, from, to) gl_des_ecb_crypt(ctx, from, to, 0) #define gl_des_ecb_decrypt(ctx, from, to) gl_des_ecb_crypt(ctx, from, to, 1) /* * Encryption/Decryption context of DES */ typedef struct { uint32_t encrypt_subkeys[32]; uint32_t decrypt_subkeys[32]; } gl_des_ctx; /* Structures to save state of computation between the single steps. */ struct md4_ctx { uint32_t A; uint32_t B; uint32_t C; uint32_t D; uint32_t total[2]; uint32_t buflen; uint32_t buffer[32]; }; struct md5_ctx { uint32_t A; uint32_t B; uint32_t C; uint32_t D; uint32_t total[2]; uint32_t buflen; uint32_t buffer[32]; }; extern bool gl_des_is_weak_key(const char * key); extern void gl_des_setkey(gl_des_ctx *ctx, const char * key); extern bool gl_des_makekey(gl_des_ctx *ctx, const char * key, size_t keylen); extern void gl_des_ecb_crypt(gl_des_ctx *ctx, const char * _from, char * _to, int mode); extern void md4_process_block (const void *buffer, size_t len, struct md4_ctx *ctx); extern void md4_init_ctx (struct md4_ctx *ctx); extern void *md4_read_ctx (const struct md4_ctx *ctx, void *resbuf); extern void *md4_finish_ctx (struct md4_ctx *ctx, void *resbuf); extern void md4_process_bytes (const void *buffer, size_t len, struct md4_ctx *ctx); extern int md4_stream(FILE * stream, void *resblock); extern void *md4_buffer (const char *buffer, size_t len, void *resblock); extern int hmac_md5 (const void *key, size_t keylen, const void *in, size_t inlen, void *resbuf); extern void md5_init_ctx (struct md5_ctx *ctx); extern void md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx); extern void md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx); extern void *md5_finish_ctx (struct md5_ctx *ctx, void *resbuf); extern void *md5_read_ctx (const struct md5_ctx *ctx, void *resbuf); extern int md5_stream (FILE *stream, void *resblock); extern void *md5_buffer (const char *buffer, size_t len, void *resblock); #endif /* _XCRYPT_H */ cntlm-0.92.3/acl.c0000664000175000017500000000567511352535506012611 0ustar daviddavid/* * These are ACL routines for the main module of CNTLM * * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #include #include #include #include #include #include #include #include "acl.h" #include "socket.h" #include "swap.h" /* * TODO: retest ACLs on big-endian */ /* * Add the rule spec to the ACL list. */ int acl_add(plist_t *rules, char *spec, enum acl_t acl) { struct in_addr source; network_t *aux; int i, mask = 32; char *tmp; if (rules == NULL) return 0; spec = strdup(spec); aux = (network_t *)new(sizeof(network_t)); i = strcspn(spec, "/"); if (i < strlen(spec)) { spec[i] = 0; mask = strtol(spec+i+1, &tmp, 10); if (mask < 0 || mask > 32 || spec[i+1] == 0 || *tmp != 0) { syslog(LOG_ERR, "ACL netmask for %s is invalid\n", spec); free(aux); free(spec); return 0; } } if (!strcmp("*", spec)) { source.s_addr = 0; mask = 0; } else { if (!strcmp("0", spec)) { source.s_addr = 0; } else if (!so_resolv(&source, spec)) { syslog(LOG_ERR, "ACL source address %s is invalid\n", spec); free(aux); free(spec); return 0; } } aux->ip = source.s_addr; aux->mask = mask; mask = swap32(~(((uint64_t)1 << (32-mask)) - 1)); if ((source.s_addr & mask) != source.s_addr) syslog(LOG_WARNING, "Subnet definition might be incorrect: %s/%d\n", inet_ntoa(source), aux->mask); syslog(LOG_INFO, "New ACL rule: %s %s/%d\n", (acl == ACL_ALLOW ? "allow" : "deny"), inet_ntoa(source), aux->mask); *rules = plist_add(*rules, acl, (char *)aux); free(spec); return 1; } /* * Takes client IP address (network order) and walks the * ACL rules until a match is found, returning ACL_ALLOW * or ACL_DENY accordingly. If no rule matches, connection * is allowed (such is the case with no ACLs). * * Proper policy should always end with a default rule, * targetting either "*" or "0/0" to explicitly express * one's intentions. */ enum acl_t acl_check(plist_t rules, struct in_addr naddr) { network_t *aux; int mask; while (rules) { aux = (network_t *)rules->aux; mask = swap32(~(((uint64_t)1 << (32-aux->mask)) - 1)); if ((naddr.s_addr & mask) == (aux->ip & mask)) return rules->key; rules = rules->next; } return ACL_ALLOW; } cntlm-0.92.3/scanner.h0000664000175000017500000000223111352535506013471 0ustar daviddavid/* * CNTLM 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. * * CNTLM 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., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2007 David Kubicek * */ #ifndef _SCANNER_H #define _SCANNER_H #include "utils.h" /* * ISA plugin flags */ #define PLUG_NONE 0x0000 #define PLUG_SENDHEAD 0x0001 #define PLUG_SENDDATA 0x0002 #define PLUG_ERROR 0x8000 #define PLUG_ALL 0x7FFF /* * Plugin download sample size */ #define SAMPLE 4096 extern int scanner_hook(rr_data_t request, rr_data_t response, struct auth_s *credentials, int cd, int *sd, long maxKBs); #endif /* _SCANNER_H */ cntlm-0.92.3/configure0000775000175000017500000000330611370745552013606 0ustar daviddavid#!/bin/sh # # Search for GCC or XL C/C++, former if both exist # # To add another compiler, just create Makefile.XXX and add XXX to $CCS # # To prevent ugly Makefile extensions, underscore and chars following it # in the name XXX are automatically removed before locating relevant # Makefile. This is why compiler "xlc_r" has Makefile extension "xlc". # This can be disabled if neccessary. # CCS="xlc_r gcc" # # Look for supported compilers # for c in $CCS; do if CCPATH=`which $c 2>&1` && [ -z "${CCPATH%%/*}" ]; then CC="$c" break fi done # # Make a link to a proper Makefile.* # if [ -z "$CC" ]; then echo "Unable to find GNU GCC or IBM XL C/C++. Fix your PATH!" exit 1 else echo "Using $CCPATH to compile Cntlm" [ -h Makefile ] && rm -f Makefile 2>/dev/null case "$CC" in gcc) # default Makefile is for GCC; just revert back to # GCC if Makefile is linked to other compiler version if [ ! -f Makefile ]; then mv Makefile.gcc Makefile fi ;; *) # backup default GCC Makefile and create a link to other if [ -f Makefile ]; then mv Makefile Makefile.gcc fi EXT=`echo "$CC" | sed 's/_.*//'` ln -s Makefile.$EXT Makefile ;; esac fi STAMP=configure-stamp CONFIG=config/config.h TESTS="endian strdup socklen_t gethostname" #[ -f $STAMP ] && exit 0 touch $STAMP rm -f $CONFIG for i in $TESTS; do printf "Checking $i... " printf "#define config_$i " >> $CONFIG OUT=`$CC -D_POSIX_C_SOURCE=199506L -D_ISOC99_SOURCE -D_REENTRANT -o config/$i config/$i.c 2>&1` rc=$? if [ $rc -ne 0 ]; then # -o -n "$OUT" ]; then rc=0 RET=no else RET=`./config/$i` rc=$? [ -z "$RET" ] && if [ $rc -eq 0 ]; then RET="no"; else RET=yes; fi fi echo $rc >> $CONFIG echo $RET done