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