2 * CNTLM is free software; you can redistribute it and/or modify it under the
3 * terms of the GNU General Public License as published by the Free Software
4 * Foundation; either version 2 of the License, or (at your option) any later
7 * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY
8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 * You should have received a copy of the GNU General Public License along with
13 * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
14 * St, Fifth Floor, Boston, MA 02110-1301, USA.
16 * Copyright (c) 2007 David Kubicek
20 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
32 #include <sys/socket.h>
45 int host_connect(const char *hostname
, int port
) {
49 if (!so_resolv(&addr
, hostname
)) {
51 // printf("so_resolv: %s failed (%d: %s)\n", hostname, h_errno, hstrerror(h_errno));
55 return so_connect(addr
, port
);
59 int www_authenticate(int sd
, rr_data_t request
, rr_data_t response
, struct auth_s
*creds
) {
60 char *tmp
, *buf
, *challenge
;
69 len
= ntlm_request(&tmp
, creds
);
71 to_base64(MEM(buf
, uint8_t, 5), MEM(tmp
, uint8_t, 0), len
, BUFSIZE
-5);
75 auth
= dup_rr_data(request
);
76 auth
->headers
= hlist_mod(auth
->headers
, "Connection", "keep-alive", 1);
77 auth
->headers
= hlist_mod(auth
->headers
, "Authorization", buf
, 1);
78 auth
->headers
= hlist_mod(auth
->headers
, "Content-Length", "0", 1);
79 auth
->headers
= hlist_del(auth
->headers
, "Transfer-Encoding");
82 * Drop whatever error page server returned
84 if (!http_body_drop(sd
, response
))
88 printf("\nSending WWW auth request...\n");
89 hlist_dump(auth
->headers
);
92 if (!headers_send(sd
, auth
))
96 printf("\nReading WWW auth response...\n");
102 if (!headers_recv(sd
, auth
)) {
107 hlist_dump(auth
->headers
);
112 if (auth
->code
== 401) {
113 if (!http_body_drop(sd
, auth
))
116 tmp
= hlist_get(auth
->headers
, "WWW-Authenticate");
117 if (tmp
&& strlen(tmp
) > 6 + 8) {
118 challenge
= new(strlen(tmp
) + 5 + 1);
119 len
= from_base64(challenge
, tmp
+ 5);
120 if (len
> NTLM_CHALLENGE_MIN
) {
121 len
= ntlm_response(&tmp
, challenge
, len
, creds
);
123 strcpy(buf
, "NTLM ");
124 to_base64(MEM(buf
, uint8_t, 5), MEM(tmp
, uint8_t, 0), len
, BUFSIZE
-5);
125 request
->headers
= hlist_mod(request
->headers
, "Authorization", buf
, 1);
128 syslog(LOG_ERR
, "No target info block. Cannot do NTLMv2!\n");
129 response
->errmsg
= "Invalid NTLM challenge from web server";
134 syslog(LOG_ERR
, "Server returning invalid challenge!\n");
135 response
->errmsg
= "Invalid NTLM challenge from web server";
142 syslog(LOG_WARNING
, "No challenge in WWW-Authenticate!\n");
143 response
->errmsg
= "Web server reply missing NTLM challenge";
151 printf("\nSending WWW auth...\n");
153 if (!headers_send(sd
, request
)) {
158 printf("\nReading final server response...\n");
161 if (!headers_recv(sd
, auth
)) {
168 hlist_dump(auth
->headers
);
172 response
= copy_rr_data(response
, auth
);
179 rr_data_t
direct_request(void *cdata
, rr_data_t request
) {
180 rr_data_t data
[2], rc
= NULL
;
181 struct auth_s
*tcreds
= NULL
;
182 int *rsocket
[2], *wsocket
[2];
186 char *hostname
= NULL
;
190 int cd
= ((struct thread_arg_s
*)cdata
)->fd
;
191 struct sockaddr_in caddr
= ((struct thread_arg_s
*)cdata
)->addr
;
194 printf("Direct thread processing...\n");
196 sd
= host_connect(request
->hostname
, request
->port
);
198 syslog(LOG_WARNING
, "Connection failed for %s:%d (%s)", request
->hostname
, request
->port
, strerror(errno
));
199 tmp
= gen_502_page(request
->http
, strerror(errno
));
200 w
= write(cd
, tmp
, strlen(tmp
));
208 * Now save NTLM credentials for purposes of this thread.
209 * If web auth fails, we'll rewrite them like with NTLM-to-Basic in proxy mode.
211 tcreds
= dup_auth(g_creds
, /* fullcopy */ 1);
213 if (request
->hostname
) {
214 hostname
= strdup(request
->hostname
);
215 port
= request
->port
;
217 tmp
= gen_502_page(request
->http
, "Invalid request URL");
218 w
= write(cd
, tmp
, strlen(tmp
));
227 data
[0] = dup_rr_data(request
);
230 data
[0] = new_rr_data();
232 data
[1] = new_rr_data();
234 rsocket
[0] = wsocket
[1] = &cd
;
235 rsocket
[1] = wsocket
[0] = &sd
;
239 for (loop
= 0; loop
< 2; ++loop
) {
240 if (data
[loop
]->empty
) { // Isn't this the first loop with request supplied by caller?
242 printf("\n******* Round %d C: %d, S: %d *******\n", loop
+1, cd
, sd
);
243 printf("Reading headers (%d)...\n", *rsocket
[loop
]);
245 if (!headers_recv(*rsocket
[loop
], data
[loop
])) {
246 free_rr_data(data
[0]);
247 free_rr_data(data
[1]);
254 * Check whether this new request still talks to the same server as previous.
255 * If no, return request to caller, he must decide on forward or direct
258 if (loop
== 0 && hostname
&& data
[0]->hostname
259 && (strcasecmp(hostname
, data
[0]->hostname
) || port
!= data
[0]->port
)) {
261 printf("\n******* D RETURN: %s *******\n", data
[0]->url
);
263 rc
= dup_rr_data(data
[0]);
264 free_rr_data(data
[0]);
265 free_rr_data(data
[1]);
270 hlist_dump(data
[loop
]->headers
);
272 if (loop
== 0 && data
[0]->req
) {
273 syslog(LOG_DEBUG
, "%s %s %s", inet_ntoa(caddr
.sin_addr
), data
[0]->method
, data
[0]->url
);
276 * Convert full proxy request URL into a relative URL
277 * Host header is already inserted by headers_recv()
279 if (data
[0]->rel_url
) {
282 data
[0]->url
= strdup(data
[0]->rel_url
);
285 data
[0]->headers
= hlist_mod(data
[0]->headers
, "Connection", "keep-alive", 1);
286 data
[0]->headers
= hlist_del(data
[0]->headers
, "Proxy-Authorization");
289 * Try to get auth from client if present
291 if (http_parse_basic(data
[0]->headers
, "Authorization", tcreds
) > 0 && debug
)
292 printf("NTLM-to-basic: Credentials parsed: %s\\%s at %s\n", tcreds
->domain
, tcreds
->user
, tcreds
->workstation
);
296 * Is this a CONNECT request?
298 if (loop
== 0 && CONNECT(data
[0])) {
300 printf("CONNECTing...\n");
305 data
[1]->msg
= strdup("Connection established");
306 data
[1]->http
= strdup(data
[0]->http
);
308 if (headers_send(cd
, data
[1]))
311 free_rr_data(data
[0]);
312 free_rr_data(data
[1]);
317 if (loop
== 1 && data
[1]->code
== 401 && hlist_subcmp_all(data
[1]->headers
, "WWW-Authenticate", "NTLM")) {
319 * Server closing the connection after 401?
320 * Should never happen.
322 if (hlist_subcmp(data
[1]->headers
, "Connection", "close")) {
324 printf("Reconnect before WWW auth\n");
326 sd
= host_connect(data
[0]->hostname
, data
[0]->port
);
328 tmp
= gen_502_page(data
[0]->http
, "WWW authentication reconnect failed");
329 w
= write(cd
, tmp
, strlen(tmp
));
336 if (!www_authenticate(*wsocket
[0], data
[0], data
[1], tcreds
)) {
338 printf("WWW auth connection error.\n");
340 tmp
= gen_502_page(data
[1]->http
, data
[1]->errmsg
? data
[1]->errmsg
: "Error during WWW-Authenticate");
341 w
= write(cd
, tmp
, strlen(tmp
));
344 free_rr_data(data
[0]);
345 free_rr_data(data
[1]);
349 } else if (data
[1]->code
== 401) {
351 * Server giving 401 after auth?
354 tmp
= gen_401_page(data
[1]->http
, data
[0]->hostname
, data
[0]->port
);
355 w
= write(cd
, tmp
, strlen(tmp
));
358 free_rr_data(data
[0]);
359 free_rr_data(data
[1]);
367 * Check if we should loop for another request. Required for keep-alive
368 * connections, client might really need a non-interrupted conversation.
370 * We default to keep-alive server connections, unless server explicitly
371 * flags closing the connection or we detect a body with unknown size
372 * (end marked by server closing).
375 conn_alive
= !hlist_subcmp(data
[1]->headers
, "Connection", "close")
376 && http_has_body(data
[0], data
[1]) != -1;
378 data
[1]->headers
= hlist_mod(data
[1]->headers
, "Proxy-Connection", "keep-alive", 1);
379 data
[1]->headers
= hlist_mod(data
[1]->headers
, "Connection", "keep-alive", 1);
381 data
[1]->headers
= hlist_mod(data
[1]->headers
, "Proxy-Connection", "close", 1);
387 printf("Sending headers (%d)...\n", *wsocket
[loop
]);
392 if (!headers_send(*wsocket
[loop
], data
[loop
])) {
393 free_rr_data(data
[0]);
394 free_rr_data(data
[1]);
399 if (!http_body_send(*wsocket
[loop
], *rsocket
[loop
], data
[0], data
[1])) {
400 free_rr_data(data
[0]);
401 free_rr_data(data
[1]);
407 free_rr_data(data
[0]);
408 free_rr_data(data
[1]);
410 } while (conn_alive
&& !so_closed(sd
) && !so_closed(cd
) && !serialize
);
423 void direct_tunnel(void *thread_data
) {
425 char *pos
, *hostname
;
427 int cd
= ((struct thread_arg_s
*)thread_data
)->fd
;
428 char *thost
= ((struct thread_arg_s
*)thread_data
)->target
;
429 struct sockaddr_in caddr
= ((struct thread_arg_s
*)thread_data
)->addr
;
431 hostname
= strdup(thost
);
432 if ((pos
= strchr(hostname
, ':')) != NULL
) {
437 sd
= host_connect(hostname
, port
);
441 syslog(LOG_DEBUG
, "%s FORWARD %s", inet_ntoa(caddr
.sin_addr
), thost
);
443 printf("Portforwarding to %s for client %d...\n", thost
, cd
);