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

 

Introduction

Documentation

Support

Miscellaneous