Squid Web Cache master
Loading...
Searching...
No Matches
negotiate_wrapper.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/*
10 * -----------------------------------------------------------------------------
11 *
12 * Author: Markus Moeller (markus_moeller at compuserve.com)
13 *
14 * Copyright (C) 2011 Markus Moeller. All rights reserved.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
29 *
30 * -----------------------------------------------------------------------------
31 */
32
33#include "squid.h"
34#include "base64.h"
35#include "compat/pipe.h"
36#include "compat/unistd.h"
37
38#include <cerrno>
39#include <cstring>
40#include <cstdlib>
41#include <ctime>
42
43#if !defined(HAVE_DECL_XMALLOC) || !HAVE_DECL_XMALLOC
44#define xmalloc malloc
45#endif
46#if !defined(HAVE_DECL_XSTRDUP) || !HAVE_DECL_XSTRDUP
47#define xstrdup strdup
48#endif
49#if !defined(HAVE_DECL_XFREE) || !HAVE_DECL_XFREE
50#define xfree free
51#endif
52
53#undef PROGRAM
54#define PROGRAM "negotiate_wrapper"
55#undef VERSION
56#define VERSION "1.0.1"
57
58#ifndef MAX_AUTHTOKEN_LEN
59#define MAX_AUTHTOKEN_LEN 65535
60#endif
61
62static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
63
64static const char *
66{
67 struct timeval now;
68 static time_t last_t = 0;
69 static char buf[128];
70
71 gettimeofday(&now, nullptr);
72 if (now.tv_sec != last_t) {
73 time_t *tmp = (time_t *) & now.tv_sec;
74 struct tm *tm = localtime(tmp);
75 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
76 last_t = now.tv_sec;
77 }
78 return buf;
79}
80
81static void
83{
84 fprintf(stderr, "Usage: \n");
85 fprintf(stderr, "negotiate_wrapper [-h] [-d] --ntlm ntlm helper + arguments --kerberos kerberos helper + arguments\n");
86 fprintf(stderr, "-h help\n");
87 fprintf(stderr, "-d full debug\n");
88 fprintf(stderr, "--ntlm full ntlm helper path with arguments\n");
89 fprintf(stderr, "--kerberos full kerberos helper path with arguments\n");
90}
91
92static void
93closeFds(FILE *a, FILE *b, FILE *c, FILE *d)
94{
95 if (a)
96 fclose(a);
97 if (b)
98 fclose(b);
99 if (c)
100 fclose(c);
101 if (d)
102 fclose(d);
103}
104
105static int
106processingLoop(FILE *FDKIN, FILE *FDKOUT, FILE *FDNIN, FILE *FDNOUT)
107{
108 char buf[MAX_AUTHTOKEN_LEN];
109 char tbuff[MAX_AUTHTOKEN_LEN];
110 char buff[MAX_AUTHTOKEN_LEN+2];
111 char *c;
112 size_t length;
113 uint8_t *token = nullptr;
114
115 while (1) {
116 if (fgets(buf, sizeof(buf) - 1, stdin) == nullptr) {
117 xfree(token);
118 if (ferror(stdin)) {
119 if (debug_enabled)
120 fprintf(stderr,
121 "%s| %s: fgets() failed! dying..... errno=%d (%s)\n",
122 LogTime(), PROGRAM, ferror(stdin),
123 strerror(ferror(stdin)));
124
125 fprintf(stdout, "BH input error\n");
126 return 1; /* BIIG buffer */
127 }
128 fprintf(stdout, "BH input error\n");
129 return 0;
130 }
131 c = strchr(buf, '\n');
132 if (c) {
133 *c = '\0';
134 length = c - buf;
135 if (debug_enabled)
136 fprintf(stderr, "%s| %s: Got '%s' from squid (length: %zu).\n",
137 LogTime(), PROGRAM, buf, length);
138 } else {
139 if (debug_enabled)
140 fprintf(stderr, "%s| %s: Oversized message\n", LogTime(),
141 PROGRAM);
142 fprintf(stdout, "BH Oversized message\n");
143 continue;
144 }
145
146 if (buf[0] == '\0') {
147 if (debug_enabled)
148 fprintf(stderr, "%s| %s: Invalid request\n", LogTime(),
149 PROGRAM);
150 fprintf(stdout, "BH Invalid request\n");
151 continue;
152 }
153 if (strlen(buf) < 2) {
154 if (debug_enabled)
155 fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(),
156 PROGRAM, buf);
157 fprintf(stdout, "BH Invalid request\n");
158 continue;
159 }
160 if (!strncmp(buf, "QQ", 2)) {
161 fprintf(stdout, "BH quit command\n");
162 xfree(token);
163 return 0;
164 }
165 if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
166 if (debug_enabled)
167 fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(),
168 PROGRAM, buf);
169 fprintf(stdout, "BH Invalid request\n");
170 continue;
171 }
172 if (strlen(buf) <= 3) {
173 if (debug_enabled)
174 fprintf(stderr, "%s| %s: Invalid negotiate request [%s]\n",
175 LogTime(), PROGRAM, buf);
176 fprintf(stdout, "BH Invalid negotiate request\n");
177 continue;
178 }
179 length = BASE64_DECODE_LENGTH(strlen(buf+3));
180 if (debug_enabled)
181 fprintf(stderr, "%s| %s: Decode '%s' (decoded length: %zu).\n",
182 LogTime(), PROGRAM, buf + 3, length);
183
184 safe_free(token);
185 if (!(token = static_cast<uint8_t *>(xmalloc(length+1)))) {
186 fprintf(stderr, "%s| %s: Error allocating memory for token\n", LogTime(), PROGRAM);
187 return 1;
188 }
189
190 struct base64_decode_ctx ctx;
191 base64_decode_init(&ctx);
192 size_t dstLen = 0;
193 if (!base64_decode_update(&ctx, &dstLen, token, strlen(buf+3), buf+3) ||
194 !base64_decode_final(&ctx)) {
195 if (debug_enabled)
196 fprintf(stderr, "%s| %s: Invalid base64 token [%s]\n", LogTime(), PROGRAM, buf+3);
197 fprintf(stdout, "BH Invalid negotiate request token\n");
198 continue;
199 }
200 assert(dstLen <= length);
201 length = dstLen;
202 token[dstLen] = '\0';
203
204 if ((static_cast<size_t>(length) >= sizeof(ntlmProtocol) + 1) &&
205 (!memcmp(token, ntlmProtocol, sizeof ntlmProtocol))) {
206 if (debug_enabled)
207 fprintf(stderr, "%s| %s: received type %d NTLM token\n",
208 LogTime(), PROGRAM, (int) *((unsigned char *) token +
209 sizeof ntlmProtocol));
210 fprintf(FDNIN, "%s\n",buf);
211 if (fgets(tbuff, sizeof(tbuff) - 1, FDNOUT) == nullptr) {
212 xfree(token);
213 if (ferror(FDNOUT)) {
214 fprintf(stderr,
215 "fgets() failed! dying..... errno=%d (%s)\n",
216 ferror(FDNOUT), strerror(ferror(FDNOUT)));
217 return 1;
218 }
219 fprintf(stderr, "%s| %s: Error reading NTLM helper response\n",
220 LogTime(), PROGRAM);
221 return 0;
222 }
223
224 if (!strchr(tbuff, '\n')) {
225 fprintf(stderr, "%s| %s: Oversized NTLM helper response\n",
226 LogTime(), PROGRAM);
227 return 0;
228 }
229
230 /*
231 * Need to translate NTLM reply to Negotiate reply:
232 * AF user => AF blob user
233 * NA reason => NA blob reason
234 * Set blob to '='
235 */
236 if (strlen(tbuff) >= 3 && (!strncmp(tbuff,"AF ",3) || !strncmp(tbuff,"NA ",3))) {
237 strncpy(buff,tbuff,3);
238 buff[3]='=';
239 for (unsigned int i=2; i<=strlen(tbuff); ++i)
240 buff[i+2] = tbuff[i];
241 } else {
242 strcpy(buff,tbuff);
243 }
244 } else {
245 if (debug_enabled)
246 fprintf(stderr, "%s| %s: received Kerberos token\n",
247 LogTime(), PROGRAM);
248
249 fprintf(FDKIN, "%s\n",buf);
250 if (fgets(buff, sizeof(buff) - 1, FDKOUT) == nullptr) {
251 xfree(token);
252 if (ferror(FDKOUT)) {
253 fprintf(stderr,
254 "fgets() failed! dying..... errno=%d (%s)\n",
255 ferror(FDKOUT), strerror(ferror(FDKOUT)));
256 return 1;
257 }
258 fprintf(stderr, "%s| %s: Error reading Kerberos helper response\n",
259 LogTime(), PROGRAM);
260 return 0;
261 }
262
263 if (!strchr(buff, '\n')) {
264 fprintf(stderr, "%s| %s: Oversized Kerberos helper response\n",
265 LogTime(), PROGRAM);
266 return 0;
267 }
268 }
269 buff[sizeof(buff)-1] = '\0'; // paranoid; already terminated correctly
270 fprintf(stdout,"%s",buff);
271 if (debug_enabled)
272 fprintf(stderr, "%s| %s: Return '%s'\n",
273 LogTime(), PROGRAM, buff);
274 }
275
276 xfree(token);
277 return 1;
278}
279
280int
281main(int argc, char *const argv[])
282{
283 int nstart = 0, kstart = 0;
284 int nend = 0, kend = 0;
285 char **nargs, **kargs;
286 int fpid;
287 int pkin[2];
288 int pkout[2];
289 int pnin[2];
290 int pnout[2];
291
292 setbuf(stdout, nullptr);
293 setbuf(stdin, nullptr);
294
295 if (argc ==1 || !strncasecmp(argv[1],"-h",2)) {
296 usage();
297 exit(EXIT_SUCCESS);
298 }
299
300 int j = 1;
301 if (!strncasecmp(argv[1],"-d",2)) {
302 debug_enabled = 1;
303 j = 2;
304 }
305
306 for (int i=j; i<argc; ++i) {
307 if (!strncasecmp(argv[i],"--ntlm",6))
308 nstart = i;
309 if (!strncasecmp(argv[i],"--kerberos",10))
310 kstart = i;
311 }
312 if (nstart > kstart) {
313 kend = nstart-1;
314 nend = argc-1;
315 } else {
316 kend = argc-1;
317 nend = kstart-1;
318 }
319 if (nstart == 0 || kstart == 0 || kend-kstart <= 0 || nend-nstart <= 0 ) {
320 usage();
321 exit(EXIT_SUCCESS);
322 }
323
324 if (debug_enabled)
325 fprintf(stderr, "%s| %s: Starting version %s\n", LogTime(), PROGRAM,
326 VERSION);
327
328 if ((nargs = (char **)xmalloc((nend-nstart+1)*sizeof(char *))) == nullptr) {
329 fprintf(stderr, "%s| %s: Error allocating memory for ntlm helper\n", LogTime(), PROGRAM);
330 exit(EXIT_FAILURE);
331 }
332 memcpy(nargs,argv+nstart+1,(nend-nstart)*sizeof(char *));
333 nargs[nend-nstart]=nullptr;
334 if (debug_enabled) {
335 fprintf(stderr, "%s| %s: NTLM command: ", LogTime(), PROGRAM);
336 for (int i=0; i<nend-nstart; ++i)
337 fprintf(stderr, "%s ", nargs[i]);
338 fprintf(stderr, "\n");
339 }
340 if ((kargs = (char **)xmalloc((kend-kstart+1)*sizeof(char *))) == nullptr) {
341 fprintf(stderr, "%s| %s: Error allocating memory for kerberos helper\n", LogTime(), PROGRAM);
342 exit(EXIT_FAILURE);
343 }
344 memcpy(kargs,argv+kstart+1,(kend-kstart)*sizeof(char *));
345 kargs[kend-kstart]=nullptr;
346 if (debug_enabled) {
347 fprintf(stderr, "%s| %s: Kerberos command: ", LogTime(), PROGRAM);
348 for (int i=0; i<kend-kstart; ++i)
349 fprintf(stderr, "%s ", kargs[i]);
350 fprintf(stderr, "\n");
351 }
352 /*
353 Fork Kerberos helper and NTLM helper and manage IO to send NTLM requests
354 to the right helper. squid must keep session state
355 */
356
357 if (pipe(pkin) < 0) {
358 fprintf(stderr, "%s| %s: Could not assign streams for pkin\n", LogTime(), PROGRAM);
359 exit(EXIT_FAILURE);
360 }
361 if (pipe(pkout) < 0) {
362 fprintf(stderr, "%s| %s: Could not assign streams for pkout\n", LogTime(), PROGRAM);
363 exit(EXIT_FAILURE);
364 }
365
366 if (( fpid = fork()) < 0 ) {
367 fprintf(stderr, "%s| %s: Failed first fork\n", LogTime(), PROGRAM);
368 exit(EXIT_FAILURE);
369 }
370
371 if ( fpid == 0 ) {
372 /* First Child for Kerberos helper */
373
374 xclose(pkin[1]);
375 dup2(pkin[0],STDIN_FILENO);
376 xclose(pkin[0]);
377
378 xclose(pkout[0]);
379 dup2(pkout[1],STDOUT_FILENO);
380 xclose(pkout[1]);
381
382 setbuf(stdin, nullptr);
383 setbuf(stdout, nullptr);
384
385 execv(kargs[0], kargs);
386 fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, kargs[0], strerror(errno));
387 exit(EXIT_FAILURE);
388 }
389
390 xclose(pkin[0]);
391 xclose(pkout[1]);
392
393 if (pipe(pnin) < 0) {
394 fprintf(stderr, "%s| %s: Could not assign streams for pnin\n", LogTime(), PROGRAM);
395 exit(EXIT_FAILURE);
396 }
397 if (pipe(pnout) < 0) {
398 fprintf(stderr, "%s| %s: Could not assign streams for pnout\n", LogTime(), PROGRAM);
399 exit(EXIT_FAILURE);
400 }
401
402 if (( fpid = fork()) < 0 ) {
403 fprintf(stderr, "%s| %s: Failed second fork\n", LogTime(), PROGRAM);
404 exit(EXIT_FAILURE);
405 }
406
407 if ( fpid == 0 ) {
408 /* Second Child for NTLM helper */
409
410 xclose(pnin[1]);
411 dup2(pnin[0],STDIN_FILENO);
412 xclose(pnin[0]);
413
414 xclose(pnout[0]);
415 dup2(pnout[1],STDOUT_FILENO);
416 xclose(pnout[1]);
417
418 setbuf(stdin, nullptr);
419 setbuf(stdout, nullptr);
420
421 execv(nargs[0], nargs);
422 fprintf(stderr, "%s| %s: Failed execv for %s: %s\n", LogTime(), PROGRAM, nargs[0], strerror(errno));
423 exit(EXIT_FAILURE);
424 }
425
426 xclose(pnin[0]);
427 xclose(pnout[1]);
428
429 FILE *FDKIN=fdopen(pkin[1],"w");
430 FILE *FDKOUT=fdopen(pkout[0],"r");
431
432 FILE *FDNIN=fdopen(pnin[1],"w");
433 FILE *FDNOUT=fdopen(pnout[0],"r");
434
435 if (!FDKIN || !FDKOUT || !FDNIN || !FDNOUT) {
436 fprintf(stderr, "%s| %s: Could not assign streams for FDKIN/FDKOUT/FDNIN/FDNOUT\n", LogTime(), PROGRAM);
437 closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT);
438 exit(EXIT_FAILURE);
439 }
440
441 setbuf(FDKIN, nullptr);
442 setbuf(FDKOUT, nullptr);
443 setbuf(FDNIN, nullptr);
444 setbuf(FDNOUT, nullptr);
445
446 int result = processingLoop(FDKIN, FDKOUT, FDNIN, FDNOUT);
447 closeFds(FDKIN, FDKOUT, FDNIN, FDNOUT);
448 return result;
449}
450
#define assert(EX)
Definition assert.h:17
void base64_decode_init(struct base64_decode_ctx *ctx)
Definition base64.c:54
int base64_decode_update(struct base64_decode_ctx *ctx, size_t *dst_length, uint8_t *dst, size_t src_length, const char *src)
Definition base64.c:129
int base64_decode_final(struct base64_decode_ctx *ctx)
Definition base64.c:159
#define BASE64_DECODE_LENGTH(length)
Definition base64.h:120
int debug_enabled
Definition debug.cc:13
int main()
static void closeFds(FILE *a, FILE *b, FILE *c, FILE *d)
#define VERSION
#define xfree
#define MAX_AUTHTOKEN_LEN
static int processingLoop(FILE *FDKIN, FILE *FDKOUT, FILE *FDNIN, FILE *FDNOUT)
static void usage()
#define xmalloc
#define PROGRAM
static const char * LogTime()
static const unsigned char ntlmProtocol[]
char * strerror(int ern)
Definition strerror.c:22
int xclose(int fd)
POSIX close(2) equivalent.
Definition unistd.h:43
#define safe_free(x)
Definition xalloc.h:73