File: ntlm.cpp

package info (click to toggle)
dansguardian 2.10.1.1-3
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 3,596 kB
  • ctags: 1,391
  • sloc: cpp: 17,633; sh: 8,892; makefile: 506; perl: 141
file content (263 lines) | stat: -rw-r--r-- 8,286 bytes parent folder | download | duplicates (5)
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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
// NTLM auth plugin

//Please refer to http://dansguardian.org/?page=copyright2
//for the license for this code.

//  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


// INCLUDES
#ifdef HAVE_CONFIG_H
	#include "dgconfig.h"
#endif

#include "../Auth.hpp"
#include "../FDTunnel.hpp"
#include "../OptionContainer.hpp"

#include <syslog.h>

#include <iconv.h>


// DEFINES

extern OptionContainer o;

// NTLM username grabbing needs to be independent of endianness

#ifdef HAVE_BYTESWAP_H
# include <byteswap.h>
# define bswap16(x) bswap_16(x)
# define bswap32(x) bswap_32(x)
#else
# ifndef bswap16
#  define bswap16(x) (((((u_int16_t)x) >> 8) & 0xff) | ((((u_int16_t)x) & 0xff) << 8))
# endif
# ifndef bswap32
#  define bswap32(x) (((((u_int32_t)x) & 0xff000000) >> 24) | ((((u_int32_t)x) & 0x00ff0000) >>  8) | \
	((((u_int32_t)x) & 0x0000ff00) <<  8) | ((((u_int32_t)x) & 0x000000ff) << 24))
# endif
#endif

#ifdef WORDS_BIGENDIAN
# define SSWAP(x) (bswap16((x)))
# define WSWAP(x) (bswap32((x)))
#else
# define SSWAP(x) (x)
# define WSWAP(x) (x)
#endif


// DECLARATIONS

// class name is relevant!
class ntlminstance:public AuthPlugin
{
public:
	// keep credentials for all requests on a given persistent connection;
	// NTLM proxy auth is designed to be used in this manner and won't re-send credentials.
	ntlminstance(ConfigVar &definition):AuthPlugin(definition) { is_connection_based = true; needs_proxy_query = true; };

	int identify(Socket& peercon, Socket& proxycon, HTTPHeader &h, std::string &string);
};

// things need to be on byte boundaries here
#pragma pack(1)
struct strhdr {
	int16_t len;
	int16_t maxlen;
	int32_t offset;
};

struct ntlmhdr {
	char signature[8]; // literally NTLMSSP\0
	int32_t type;      // 1, 2 or 3, auth resposes are type 3.
};

// this struct is only valid if h.type == 3
// as we only evesdrop to get userid dont care about type 1 and 2 messages
struct ntlm_auth {
	ntlmhdr h;
	strhdr lmresponse;          // LANMAN challenge response
	strhdr ntresponse;          // NT challenge response
	strhdr domain;              // Domain to authenticate against
	strhdr user;                // Username (only thing we care about atm.)
	strhdr workstation;         // Workstation name
	strhdr sessionkey;          // Session key for server's use
	int32_t flags;              // Request flags
	char payload[256 * 6];      // String data - enough for everything at 255 chars
	                            // but packet does not need to be that big
};

// union so load data into buffer and the byte aligned struct gets
// filled in.
union ntlm_authenticate {
	ntlm_auth a;
	char buf[sizeof(ntlm_auth)];
};
#pragma pack()

// "template adaptor" for iconv - basically, let G++ do the hard work of
// figuring out whether or not the second parameter is const for us ;)
template <typename T>
inline size_t local_iconv_adaptor (size_t (*iconv_func)(iconv_t, T, size_t *, char**,size_t*),
	iconv_t cd, char **inbuf, size_t *inbytesleft,
	char **outbuf, size_t *outbytesleft)
{
  return iconv_func (cd, (T)inbuf, inbytesleft, outbuf, outbytesleft);
}


// IMPLEMENTATION

// class factory code *MUST* be included in every plugin

AuthPlugin *ntlmcreate(ConfigVar & definition)
{
	return new ntlminstance(definition);
}

// end of Class factory

// ntlm auth header username extraction - also lets connection persist long enough to complete NTLM negotiation
int ntlminstance::identify(Socket& peercon, Socket& proxycon, HTTPHeader &h, std::string &string)
{
	FDTunnel fdt;

	String at(h.getAuthType());
	if (at != "NTLM") {
		// if no auth currently underway, then...
		if (at.length() == 0) {
			// allow the initial request through so the client will get the proxy's initial auth required response.
			// advertise persistent connections so that parent proxy will agree to advertise NTLM support.
#ifdef DGDEBUG
			std::cout << "No auth negotiation currently in progress - making initial request persistent so that proxy will advertise NTLM" << std::endl;
#endif

			h.makePersistent();
		}
		return DGAUTH_NOMATCH;
	}

#ifdef DGDEBUG
	std::cout << "NTLM - sending step 1" << std::endl;
#endif
	if (o.forwarded_for) {
		std::string clientip;
		if (o.use_xforwardedfor) {
			// grab the X-Forwarded-For IP if available
			clientip = h.getXForwardedForIP();
			// otherwise, grab the IP directly from the client connection
			if (clientip.length() == 0)
				clientip = peercon.getPeerIP();
		} else {
			clientip = peercon.getPeerIP();
		}
		h.addXForwardedFor(clientip);  // add squid-like entry
	}
	h.makePersistent();
	h.out(&peercon, &proxycon, __DGHEADER_SENDALL);
#ifdef DGDEBUG
	std::cout << "NTLM - receiving step 2" << std::endl;
#endif
	h.in(&proxycon, true);

	if (h.authRequired()) {
#ifdef DGDEBUG
		std::cout << "NTLM - sending step 2" << std::endl;
#endif
		h.out(NULL, &peercon, __DGHEADER_SENDALL);
		if (h.contentLength() != -1)
			fdt.tunnel(proxycon, peercon, false, h.contentLength(), true);
#ifdef DGDEBUG
		std::cout << "NTLM - receiving step 3" << std::endl;
#endif
		h.in(&peercon, true);

#ifdef DGDEBUG
		std::cout << "NTLM - decoding type 3 message" << std::endl;
#endif

		std::string message(h.getAuthData());

		ntlm_authenticate auth;
		ntlm_auth *a = &(auth.a);
		static char username[256]; // fixed size
		static char username2[256];
		char* inptr = username;
		char* outptr = username2;
		size_t l,o;

		// copy the NTLM message into the union's buffer, simultaneously filling in the struct
		if ((message.length() > sizeof(ntlm_auth)) || (message.length() < offsetof(ntlm_auth, payload))) {
			syslog(LOG_ERR, "NTLM - Invalid message of length %zd, message was: %s", message.length(), message.c_str());
#ifdef DGDEBUG
			std::cerr << "NTLM - Invalid message of length " << message.length() << ", message was: " << message << std::endl;
#endif
			return -3;
		}
		memcpy((void *)auth.buf, (const void *)message.c_str(), message.length());

		// verify that the message is indeed a type 3
		if (strcmp("NTLMSSP",a->h.signature) == 0 && WSWAP(a->h.type) == 3) {
			// grab the length & offset of the username within the message
			// cope with the possibility we are a different byte order to Windows
			l = SSWAP(a->user.len);
			o = WSWAP(a->user.offset);

			if ((l > 0) && (o >= 0) && (o + l) <= sizeof(a->payload) && (l <= 254)) {
				// everything is in range
				// note offsets are from start of packet - not the start of the payload area
				memcpy((void *)username, (const void *)&(auth.buf[o]),l);
				username[l] = '\0';
				// check flags - we may need to convert from UTF-16 to something more sensible
				int f = WSWAP(a->flags);
				if (f & WSWAP(0x0001)) {
					iconv_t ic = iconv_open("UTF-8", "UTF-16LE");
					if (ic == (iconv_t)-1) {
						syslog(LOG_ERR, "NTLM - Cannot initialise conversion from UTF-16LE to UTF-8: %s", strerror(errno));
#ifdef DGDEBUG
						std::cerr << "NTLM - Cannot initialise conversion from UTF-16LE to UTF-8: " << strerror(errno) << std::endl;
#endif
						iconv_close(ic);
						return -2;
					}
					size_t l2 = 256;
					local_iconv_adaptor(iconv, ic, &inptr, &l, &outptr, &l2);
					iconv_close(ic);
					username2[256 - l2] = '\0';
#ifdef DGDEBUG
					std::cout << "NTLM - got username (converted from UTF-16LE) " << username2 << std::endl;
#endif
					string = username2;
				} else {
#ifdef DGDEBUG
					std::cout << "NTLM - got username " << username << std::endl;
#endif
					string = username;
				}
				return DGAUTH_OK;
			}
		}
		return DGAUTH_NOMATCH;
	} else {
#ifdef DGDEBUG
		for (unsigned int i = 0; i < h.header.size(); i++)
			std::cout << h.header[i] << std::endl;
#endif
		return -1;
	}
}