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