negotiate_kerberos_auth.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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) 2007 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  * As a special exemption, M Moeller gives permission to link this program
31  * with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
32  * the resulting executable, without including the source code for
33  * the Libraries in the source distribution.
34  *
35  * -----------------------------------------------------------------------------
36  */
37 
38 #include "squid.h"
39 #include "rfc1738.h"
40 
41 #if HAVE_GSSAPI
42 
43 #include "negotiate_kerberos.h"
44 
45 #if HAVE_SYS_STAT_H
46 #include "sys/stat.h"
47 #endif
48 #if HAVE_UNISTD_H
49 #include "unistd.h"
50 #endif
51 
52 #if HAVE_KRB5_MEMORY_KEYTAB
53 typedef struct _krb5_kt_list {
54  struct _krb5_kt_list *next;
55  krb5_keytab_entry *entry;
56 } *krb5_kt_list;
57 krb5_kt_list ktlist = nullptr;
58 
59 krb5_keytab memory_keytab;
60 
61 krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list kt_list);
62 krb5_error_code krb5_write_keytab(krb5_context context,
63  krb5_kt_list kt_list,
64  char *name);
65 krb5_error_code krb5_read_keytab(krb5_context context,
66  char *name,
67  krb5_kt_list *kt_list);
68 #endif /* HAVE_KRB5_MEMORY_KEYTAB */
69 
70 int
71 check_k5_err(krb5_context context, const char *function, krb5_error_code code)
72 {
73 
74  if (code && code != KRB5_KT_END) {
75  const char *errmsg;
76  errmsg = krb5_get_error_message(context, code);
77  debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, errmsg);
78  fprintf(stderr, "%s| %s: ERROR: %s: %s\n", LogTime(), PROGRAM, function, errmsg);
79 #if HAVE_KRB5_FREE_ERROR_MESSAGE
80  krb5_free_error_message(context, errmsg);
81 #elif HAVE_KRB5_FREE_ERROR_STRING
82  krb5_free_error_string(context, (char *)errmsg);
83 #else
84  xfree(errmsg);
85 #endif
86  }
87  return code;
88 }
89 
90 char *
91 gethost_name(void)
92 {
93  /*
94  * char hostname[sysconf(_SC_HOST_NAME_MAX)];
95  */
96  char hostname[1024];
97  struct addrinfo *hres = nullptr, *hres_list;
98  int rc, count;
99 
100  rc = gethostname(hostname, sizeof(hostname)-1);
101  if (rc) {
102  debug((char *) "%s| %s: ERROR: resolving hostname '%s' failed\n", LogTime(), PROGRAM, hostname);
103  fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
104  LogTime(), PROGRAM, hostname);
105  return nullptr;
106  }
107  rc = getaddrinfo(hostname, nullptr, nullptr, &hres);
108  if (rc != 0 || hres == nullptr ) {
109  debug((char *) "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
110  LogTime(), PROGRAM, gai_strerror(rc));
111  fprintf(stderr,
112  "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
113  LogTime(), PROGRAM, gai_strerror(rc));
114  return nullptr;
115  }
116  hres_list = hres;
117  count = 0;
118  while (hres_list) {
119  ++count;
120  hres_list = hres_list->ai_next;
121  }
122  rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
123  sizeof(hostname), nullptr, 0, 0);
124  if (rc != 0) {
125  debug((char *) "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
126  LogTime(), PROGRAM, gai_strerror(rc));
127  fprintf(stderr,
128  "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
129  LogTime(), PROGRAM, gai_strerror(rc));
130  freeaddrinfo(hres);
131  return nullptr;
132  }
133  freeaddrinfo(hres);
134  hostname[sizeof(hostname)-1] = '\0';
135  return (xstrdup(hostname));
136 }
137 
138 int
139 check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
140  const char *function, int log, int sout)
141 {
142  if (GSS_ERROR(major_status)) {
143  OM_uint32 maj_stat, min_stat;
144  OM_uint32 msg_ctx = 0;
145  gss_buffer_desc status_string;
146  char buf[1024];
147  size_t len;
148 
149  len = 0;
150  msg_ctx = 0;
151  do {
152  /* convert major status code (GSS-API error) to text */
153  maj_stat = gss_display_status(&min_stat, major_status,
154  GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
155  if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
156  if (sizeof(buf) > len + status_string.length + 1) {
157  snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
158  len += status_string.length;
159  }
160  } else
161  msg_ctx = 0;
162  gss_release_buffer(&min_stat, &status_string);
163  } while (msg_ctx);
164  if (sizeof(buf) > len + 2) {
165  snprintf(buf + len, (sizeof(buf) - len), "%s", ". ");
166  len += 2;
167  }
168  msg_ctx = 0;
169  do {
170  /* convert minor status code (underlying routine error) to text */
171  maj_stat = gss_display_status(&min_stat, minor_status,
172  GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
173  if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
174  if (sizeof(buf) > len + status_string.length) {
175  snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
176  len += status_string.length;
177  }
178  } else
179  msg_ctx = 0;
180  gss_release_buffer(&min_stat, &status_string);
181  } while (msg_ctx);
182  debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf);
183  if (sout)
184  fprintf(stdout, "BH %s failed: %s\n", function, buf);
185  if (log)
186  fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(),
187  PROGRAM);
188  return (1);
189  }
190  return (0);
191 }
192 
193 #if HAVE_KRB5_MEMORY_KEYTAB
194 /*
195  * Free a kt_list
196  */
197 krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list list)
198 {
199  krb5_kt_list lp = list;
200 
201  while (lp) {
202 #if USE_HEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY )
203  krb5_error_code retval = krb5_kt_free_entry(context, lp->entry);
204 #else
205  krb5_error_code retval = krb5_free_keytab_entry_contents(context, lp->entry);
206 #endif
207  safe_free(lp->entry);
208  if (check_k5_err(context, "krb5_kt_free_entry", retval))
209  return retval;
210  krb5_kt_list prev = lp;
211  lp = lp->next;
212  xfree(prev);
213  }
214  return 0;
215 }
216 /*
217  * Read in a keytab and append it to list. If list starts as NULL,
218  * allocate a new one if necessary.
219  */
220 krb5_error_code krb5_read_keytab(krb5_context context, char *name, krb5_kt_list *list)
221 {
222  krb5_kt_list lp = nullptr, tail = nullptr, back = nullptr;
223  krb5_keytab kt;
224  krb5_keytab_entry *entry;
225  krb5_kt_cursor cursor;
226  krb5_error_code retval = 0;
227 
228  if (*list) {
229  /* point lp at the tail of the list */
230  for (lp = *list; lp->next; lp = lp->next);
231  back = lp;
232  }
233  retval = krb5_kt_resolve(context, name, &kt);
234  if (check_k5_err(context, "krb5_kt_resolve", retval))
235  return retval;
236  retval = krb5_kt_start_seq_get(context, kt, &cursor);
237  if (check_k5_err(context, "krb5_kt_start_seq_get", retval))
238  goto close_kt;
239  for (;;) {
240  entry = (krb5_keytab_entry *)xcalloc(1, sizeof (krb5_keytab_entry));
241  if (!entry) {
242  retval = ENOMEM;
243  debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
244  LogTime(), PROGRAM, strerror(retval));
245  fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
246  LogTime(), PROGRAM, strerror(retval));
247  break;
248  }
249  memset(entry, 0, sizeof (*entry));
250  retval = krb5_kt_next_entry(context, kt, entry, &cursor);
251  if (check_k5_err(context, "krb5_kt_next_entry", retval))
252  break;
253 
254  if (!lp) { /* if list is empty, start one */
255  lp = (krb5_kt_list)xmalloc(sizeof (*lp));
256  if (!lp) {
257  retval = ENOMEM;
258  debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
259  LogTime(), PROGRAM, strerror(retval));
260  fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
261  LogTime(), PROGRAM, strerror(retval));
262  break;
263  }
264  } else {
265  lp->next = (krb5_kt_list)xmalloc(sizeof (*lp));
266  if (!lp->next) {
267  retval = ENOMEM;
268  debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
269  LogTime(), PROGRAM, strerror(retval));
270  fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
271  LogTime(), PROGRAM, strerror(retval));
272  break;
273  }
274  lp = lp->next;
275  }
276  if (!tail)
277  tail = lp;
278  lp->next = nullptr;
279  lp->entry = entry;
280  }
281  xfree(entry);
282  if (retval) {
283  if (retval == KRB5_KT_END)
284  retval = 0;
285  else {
286  krb5_free_kt_list(context, tail);
287  tail = nullptr;
288  if (back)
289  back->next = nullptr;
290  }
291  }
292  if (!*list)
293  *list = tail;
294  krb5_kt_end_seq_get(context, kt, &cursor);
295 close_kt:
296  krb5_kt_close(context, kt);
297  return retval;
298 }
299 
300 /*
301  * Takes a kt_list and writes it to the named keytab.
302  */
303 krb5_error_code krb5_write_keytab(krb5_context context, krb5_kt_list list, char *name)
304 {
305  char ktname[MAXPATHLEN+sizeof("MEMORY:")+1];
306  krb5_error_code retval = 0;
307 
308  snprintf(ktname, sizeof(ktname), "%s", name);
309  retval = krb5_kt_resolve(context, ktname, &memory_keytab);
310  if (retval)
311  return retval;
312  for (krb5_kt_list lp = list; lp; lp = lp->next) {
313  retval = krb5_kt_add_entry(context, memory_keytab, lp->entry);
314  if (retval)
315  break;
316  }
317  /*
318  * krb5_kt_close(context, kt);
319  */
320  return retval;
321 }
322 #endif /* HAVE_KRB5_MEMORY_KEYTAB */
323 
324 int
325 main(int argc, char *const argv[])
326 {
327  char buf[MAX_AUTHTOKEN_LEN];
328  char *c, *p;
329  char *user = nullptr;
330  char *rfc_user = nullptr;
331 #if HAVE_PAC_SUPPORT
332  char ad_groups[MAX_PAC_GROUP_SIZE];
333  char *ag=nullptr;
334  krb5_pac pac;
335 #if USE_HEIMDAL_KRB5
336  gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
337 #else
338  gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
339 #endif
340 #endif
341  krb5_context context = nullptr;
342  krb5_error_code ret;
343  long length = 0;
344  static int err = 0;
345  int opt, log = 0, norealm = 0;
346  OM_uint32 ret_flags = 0, spnego_flag = 0;
347  char *service_name = (char *) "HTTP", *host_name = nullptr;
348  char *token = nullptr;
349  char *service_principal = nullptr;
350  char *keytab_name = nullptr;
351  char *keytab_name_env = nullptr;
352  char default_keytab[MAXPATHLEN] = {};
353 #if HAVE_KRB5_MEMORY_KEYTAB
354  char *memory_keytab_name = nullptr;
355  char *memory_keytab_name_env = nullptr;
356 #endif
357  char *rcache_type = nullptr;
358  char *rcache_type_env = nullptr;
359  char *rcache_dir = nullptr;
360  char *rcache_dir_env = nullptr;
361  OM_uint32 major_status, minor_status;
362  gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
363  gss_name_t client_name = GSS_C_NO_NAME;
364  gss_name_t server_name = GSS_C_NO_NAME;
365  gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
366  gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
367  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
368  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
369  const unsigned char *kerberosToken = nullptr;
370  const unsigned char *spnegoToken = nullptr;
371  size_t spnegoTokenLength = 0;
372 
373  setbuf(stdout, nullptr);
374  setbuf(stdin, nullptr);
375 
376  while (-1 != (opt = getopt(argc, argv, "dirs:k:c:t:"))) {
377  switch (opt) {
378  case 'd':
379  debug_enabled = 1;
380  break;
381  case 'i':
382  log = 1;
383  break;
384  case 'r':
385  norealm = 1;
386  break;
387  case 'k':
388 #if HAVE_SYS_STAT_H
389  struct stat fstat;
390  char *ktp;
391 #endif
392  if (optarg)
393  keytab_name = xstrdup(optarg);
394  else {
395  fprintf(stderr, "ERROR: keytab file not given\n");
396  exit(EXIT_FAILURE);
397  }
398  /*
399  * Some sanity checks
400  */
401 #if HAVE_SYS_STAT_H
402  if ((ktp=strchr(keytab_name,':')))
403  ktp++;
404  else
405  ktp=keytab_name;
406  if (stat((const char*)ktp, &fstat)) {
407  if (ENOENT == errno)
408  fprintf(stderr, "ERROR: keytab file %s does not exist\n",keytab_name);
409  else
410  fprintf(stderr, "ERROR: Error %s during stat of keytab file %s\n",strerror(errno),keytab_name);
411  exit(EXIT_FAILURE);
412  } else if (!S_ISREG(fstat.st_mode)) {
413  fprintf(stderr, "ERROR: keytab file %s is not a file\n",keytab_name);
414  exit(EXIT_FAILURE);
415  }
416 #endif
417 #if HAVE_UNISTD_H
418  if (access(ktp, R_OK)) {
419  fprintf(stderr, "ERROR: keytab file %s is not accessible\n",keytab_name);
420  exit(EXIT_FAILURE);
421  }
422 #endif
423  break;
424  case 'c':
425 #if HAVE_SYS_STAT_H
426  struct stat dstat;
427 #endif
428  if (optarg)
429  rcache_dir = xstrdup(optarg);
430  else {
431  fprintf(stderr, "ERROR: replay cache directory not given\n");
432  exit(EXIT_FAILURE);
433  }
434  /*
435  * Some sanity checks
436  */
437 #if HAVE_SYS_STAT_H
438  if (stat((const char*)rcache_dir, &dstat)) {
439  if (ENOENT == errno)
440  fprintf(stderr, "ERROR: replay cache directory %s does not exist\n",rcache_dir);
441  else
442  fprintf(stderr, "ERROR: Error %s during stat of replay cache directory %s\n",strerror(errno),rcache_dir);
443  exit(EXIT_FAILURE);
444  } else if (!S_ISDIR(dstat.st_mode)) {
445  fprintf(stderr, "ERROR: replay cache directory %s is not a directory\n",rcache_dir);
446  exit(EXIT_FAILURE);
447  }
448 #endif
449 #if HAVE_UNISTD_H
450  if (access(rcache_dir, W_OK)) {
451  fprintf(stderr, "ERROR: replay cache directory %s is not accessible\n",rcache_dir);
452  exit(EXIT_FAILURE);
453  }
454 #endif
455  break;
456  case 't':
457  if (optarg)
458  rcache_type = xstrdup(optarg);
459  else {
460  fprintf(stderr, "ERROR: replay cache type not given\n");
461  exit(EXIT_FAILURE);
462  }
463  break;
464  case 's':
465  if (optarg)
466  service_principal = xstrdup(optarg);
467  else {
468  fprintf(stderr, "ERROR: service principal not given\n");
469  exit(EXIT_FAILURE);
470  }
471  break;
472  default:
473  fprintf(stderr, "Usage: \n");
474  fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n");
475  fprintf(stderr, "-d full debug\n");
476  fprintf(stderr, "-i informational messages\n");
477  fprintf(stderr, "-r remove realm from username\n");
478  fprintf(stderr, "-s service principal name\n");
479  fprintf(stderr, "-k keytab name\n");
480  fprintf(stderr, "-c replay cache directory\n");
481  fprintf(stderr, "-t replay cache type\n");
482  fprintf(stderr,
483  "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
484  fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
485  exit(EXIT_SUCCESS);
486  }
487  }
488 
489  debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION);
490  if (service_principal && strcasecmp(service_principal, "GSS_C_NO_NAME")) {
491  if (!strstr(service_principal,"HTTP/")) {
492  debug((char *) "%s| %s: WARN: service_principal %s does not start with HTTP/\n",
493  LogTime(), PROGRAM, service_principal);
494  }
495  service.value = service_principal;
496  service.length = strlen((char *) service.value);
497  } else {
498  host_name = gethost_name();
499  if (!host_name) {
500  fprintf(stderr,
501  "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
502  LogTime(), PROGRAM);
503  fprintf(stdout, "BH hostname error\n");
504  exit(EXIT_FAILURE);
505  }
506  service.value = xmalloc(strlen(service_name) + strlen(host_name) + 2);
507  snprintf((char *) service.value, strlen(service_name) + strlen(host_name) + 2,
508  "%s@%s", service_name, host_name);
509  service.length = strlen((char *) service.value);
510  xfree(host_name);
511  }
512 
513  if (rcache_type) {
514  rcache_type_env = (char *) xmalloc(strlen("KRB5RCACHETYPE=")+strlen(rcache_type)+1);
515  strcpy(rcache_type_env, "KRB5RCACHETYPE=");
516  strcat(rcache_type_env, rcache_type);
517  putenv(rcache_type_env);
518  debug((char *) "%s| %s: INFO: Setting replay cache type to %s\n",
519  LogTime(), PROGRAM, rcache_type);
520  }
521 
522  if (rcache_dir) {
523  rcache_dir_env = (char *) xmalloc(strlen("KRB5RCACHEDIR=")+strlen(rcache_dir)+1);
524  strcpy(rcache_dir_env, "KRB5RCACHEDIR=");
525  strcat(rcache_dir_env, rcache_dir);
526  putenv(rcache_dir_env);
527  debug((char *) "%s| %s: INFO: Setting replay cache directory to %s\n",
528  LogTime(), PROGRAM, rcache_dir);
529  }
530 
531  if (keytab_name) {
532  keytab_name_env = (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(keytab_name)+1);
533  strcpy(keytab_name_env, "KRB5_KTNAME=");
534  strcat(keytab_name_env, keytab_name);
535  putenv(keytab_name_env);
536  } else {
537  keytab_name_env = getenv("KRB5_KTNAME");
538  if (!keytab_name_env) {
539  ret = krb5_init_context(&context);
540  if (!check_k5_err(context, "krb5_init_context", ret)) {
541  krb5_kt_default_name(context, default_keytab, MAXPATHLEN);
542  }
543  keytab_name = xstrdup(default_keytab);
544  krb5_free_context(context);
545  } else
546  keytab_name = xstrdup(keytab_name_env);
547  }
548  debug((char *) "%s| %s: INFO: Setting keytab to %s\n", LogTime(), PROGRAM, keytab_name);
549 #if HAVE_KRB5_MEMORY_KEYTAB
550  ret = krb5_init_context(&context);
551  if (!check_k5_err(context, "krb5_init_context", ret)) {
552  memory_keytab_name = (char *)xmalloc(strlen("MEMORY:negotiate_kerberos_auth_")+16);
553  snprintf(memory_keytab_name, strlen("MEMORY:negotiate_kerberos_auth_")+16,
554  "MEMORY:negotiate_kerberos_auth_%d", (unsigned int) getpid());
555  ret = krb5_read_keytab(context, keytab_name, &ktlist);
556  if (check_k5_err(context, "krb5_read_keytab", ret)) {
557  debug((char *) "%s| %s: ERROR: Reading keytab %s into list failed\n",
558  LogTime(), PROGRAM, keytab_name);
559  } else {
560  ret = krb5_write_keytab(context, ktlist, memory_keytab_name);
561  if (check_k5_err(context, "krb5_write_keytab", ret)) {
562  debug((char *) "%s| %s: ERROR: Writing list into keytab %s\n",
563  LogTime(), PROGRAM, memory_keytab_name);
564  } else {
565  memory_keytab_name_env = (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(memory_keytab_name)+1);
566  strcpy(memory_keytab_name_env, "KRB5_KTNAME=");
567  strcat(memory_keytab_name_env, memory_keytab_name);
568  putenv(memory_keytab_name_env);
569  xfree(keytab_name);
570  keytab_name = xstrdup(memory_keytab_name);
571  debug((char *) "%s| %s: INFO: Changed keytab to %s\n",
572  LogTime(), PROGRAM, memory_keytab_name);
573  }
574  }
575  ret = krb5_free_kt_list(context,ktlist);
576  if (check_k5_err(context, "krb5_free_kt_list", ret)) {
577  debug((char *) "%s| %s: ERROR: Freeing list failed\n",
578  LogTime(), PROGRAM);
579  }
580  }
581  krb5_free_context(context);
582 #endif
583 #ifdef HAVE_HEIMDAL_KERBEROS
584  gsskrb5_register_acceptor_identity(keytab_name);
585 #endif
586  while (1) {
587  if (fgets(buf, sizeof(buf) - 1, stdin) == nullptr) {
588  if (ferror(stdin)) {
589  debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
590  LogTime(), PROGRAM, ferror(stdin),
591  strerror(ferror(stdin)));
592 
593  fprintf(stdout, "BH input error\n");
594  exit(EXIT_FAILURE); /* BIIG buffer */
595  }
596  fprintf(stdout, "BH input error\n");
597  exit(EXIT_SUCCESS);
598  }
599  c = (char *) memchr(buf, '\n', sizeof(buf) - 1);
600  if (c) {
601  *c = '\0';
602  length = c - buf;
603  } else {
604  err = 1;
605  }
606  if (err) {
607  debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM);
608  fprintf(stdout, "BH Oversized message\n");
609  err = 0;
610  continue;
611  }
612  debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", LogTime(), PROGRAM, buf, length);
613 
614  if (buf[0] == '\0') {
615  debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM);
616  fprintf(stdout, "BH Invalid request\n");
617  continue;
618  }
619  if (strlen(buf) < 2) {
620  debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
621  fprintf(stdout, "BH Invalid request\n");
622  continue;
623  }
624  if (!strncmp(buf, "QQ", 2)) {
625  gss_release_buffer(&minor_status, &input_token);
626  gss_release_buffer(&minor_status, &output_token);
627  gss_release_buffer(&minor_status, &service);
628  gss_release_cred(&minor_status, &server_creds);
629  if (server_name)
630  gss_release_name(&minor_status, &server_name);
631  if (client_name)
632  gss_release_name(&minor_status, &client_name);
633  if (gss_context != GSS_C_NO_CONTEXT)
634  gss_delete_sec_context(&minor_status, &gss_context, nullptr);
635  if (kerberosToken) {
636  /* Allocated by parseNegTokenInit, but no matching free function exists.. */
637  if (!spnego_flag)
638  xfree(kerberosToken);
639  }
640  if (spnego_flag) {
641  /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
642  xfree(spnegoToken);
643  }
644  xfree(token);
645  xfree(rcache_type);
646  xfree(rcache_type_env);
647  xfree(rcache_dir);
648  xfree(rcache_dir_env);
649  xfree(keytab_name);
650  xfree(keytab_name_env);
651 #if HAVE_KRB5_MEMORY_KEYTAB
652  krb5_kt_close(context, memory_keytab);
653  xfree(memory_keytab_name);
654  xfree(memory_keytab_name_env);
655 #endif
656  xfree(rfc_user);
657  fprintf(stdout, "BH quit command\n");
658  exit(EXIT_SUCCESS);
659  }
660  if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
661  debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
662  fprintf(stdout, "BH Invalid request\n");
663  continue;
664  }
665  if (!strncmp(buf, "YR", 2)) {
666  if (gss_context != GSS_C_NO_CONTEXT)
667  gss_delete_sec_context(&minor_status, &gss_context, nullptr);
668  gss_context = GSS_C_NO_CONTEXT;
669  }
670  if (strlen(buf) <= 3) {
671  debug((char *) "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf);
672  fprintf(stdout, "BH Invalid negotiate request\n");
673  continue;
674  }
675  const char *b64Token = buf+3;
676  const size_t srcLen = strlen(buf+3);
677  input_token.length = BASE64_DECODE_LENGTH(srcLen);
678  debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n",
679  LogTime(), PROGRAM, b64Token, (int) input_token.length);
680  input_token.value = xmalloc(input_token.length);
681 
682  struct base64_decode_ctx ctx;
683  base64_decode_init(&ctx);
684  size_t dstLen = 0;
685  if (!base64_decode_update(&ctx, &dstLen, static_cast<uint8_t*>(input_token.value), srcLen, b64Token) ||
686  !base64_decode_final(&ctx)) {
687  debug((char *) "%s| %s: ERROR: Invalid base64 token [%s]\n", LogTime(), PROGRAM, b64Token);
688  fprintf(stdout, "BH Invalid negotiate request token\n");
689  continue;
690  }
691  input_token.length = dstLen;
692 
693  if ((input_token.length >= sizeof ntlmProtocol + 1) &&
694  (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
695  debug((char *) "%s| %s: WARNING: received type %d NTLM token\n",
696  LogTime(), PROGRAM,
697  (int) *((unsigned char *) input_token.value +
698  sizeof ntlmProtocol));
699  fprintf(stdout, "BH received type %d NTLM token\n",
700  (int) *((unsigned char *) input_token.value +
701  sizeof ntlmProtocol));
702  goto cleanup;
703  }
704  if (service_principal) {
705  if (strcasecmp(service_principal, "GSS_C_NO_NAME")) {
706  major_status = gss_import_name(&minor_status, &service,
707  (gss_OID) GSS_C_NULL_OID, &server_name);
708 
709  } else {
710  server_name = GSS_C_NO_NAME;
711  major_status = GSS_S_COMPLETE;
712  minor_status = 0;
713  }
714  } else {
715  major_status = gss_import_name(&minor_status, &service,
716  gss_nt_service_name, &server_name);
717  }
718 
719  if (check_gss_err(major_status, minor_status, "gss_import_name()", log, 1))
720  goto cleanup;
721 
722  major_status =
723  gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
724  GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, nullptr, nullptr);
725  if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log, 1))
726  goto cleanup;
727 
728  major_status = gss_accept_sec_context(&minor_status,
729  &gss_context,
730  server_creds,
731  &input_token,
732  GSS_C_NO_CHANNEL_BINDINGS,
733  &client_name, nullptr, &output_token, &ret_flags, nullptr, nullptr);
734 
735  if (output_token.length) {
736  spnegoToken = (const unsigned char *) output_token.value;
737  spnegoTokenLength = output_token.length;
738  token = (char *) xmalloc((size_t)base64_encode_len(spnegoTokenLength));
739  if (token == nullptr) {
740  debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
741  fprintf(stdout, "BH Not enough memory\n");
742  goto cleanup;
743  }
744  struct base64_encode_ctx tokCtx;
745  base64_encode_init(&tokCtx);
746  size_t blen = base64_encode_update(&tokCtx, token, spnegoTokenLength, reinterpret_cast<const uint8_t*>(spnegoToken));
747  blen += base64_encode_final(&tokCtx, token+blen);
748  token[blen] = '\0';
749 
750  if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1))
751  goto cleanup;
752  if (major_status & GSS_S_CONTINUE_NEEDED) {
753  debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
754  fprintf(stdout, "TT token=%s\n", token);
755  goto cleanup;
756  }
757  gss_release_buffer(&minor_status, &output_token);
758  major_status =
759  gss_display_name(&minor_status, client_name, &output_token,
760  nullptr);
761 
762  if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1))
763  goto cleanup;
764  user = (char *) xmalloc(output_token.length + 1);
765  if (user == nullptr) {
766  debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
767  fprintf(stdout, "BH Not enough memory\n");
768  goto cleanup;
769  }
770  memcpy(user, output_token.value, output_token.length);
771  user[output_token.length] = '\0';
772  if (norealm && (p = strchr(user, '@')) != nullptr) {
773  *p = '\0';
774  }
775 
776 #if HAVE_PAC_SUPPORT
777  ret = krb5_init_context(&context);
778  if (!check_k5_err(context, "krb5_init_context", ret)) {
779 #if USE_HEIMDAL_KRB5
780 #define ADWIN2KPAC 128
781  major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status,
782  gss_context, ADWIN2KPAC, &data_set);
783  if (!check_gss_err(major_status, minor_status,
784  "gsskrb5_extract_authz_data_from_sec_context()", log, 0)) {
785  ret = krb5_pac_parse(context, data_set.value, data_set.length, &pac);
786  gss_release_buffer(&minor_status, &data_set);
787  if (!check_k5_err(context, "krb5_pac_parse", ret)) {
788  ag = get_ad_groups((char *)&ad_groups, context, pac);
789  krb5_pac_free(context, pac);
790  }
791  krb5_free_context(context);
792  }
793 #else
794  type_id.value = (void *)"mspac";
795  type_id.length = strlen((char *)type_id.value);
796 #define KRB5PACLOGONINFO 1
797  major_status = gss_map_name_to_any(&minor_status, client_name, KRB5PACLOGONINFO, &type_id, (gss_any_t *)&pac);
798  if (!check_gss_err(major_status, minor_status, "gss_map_name_to_any()", log, 0)) {
799  ag = get_ad_groups((char *)&ad_groups,context, pac);
800  }
801  (void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac);
802  krb5_free_context(context);
803 #endif
804  }
805  if (ag) {
806  debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM, ag);
807  }
808 #endif
809  rfc_user = rfc1738_escape(user);
810 #if HAVE_PAC_SUPPORT
811  fprintf(stdout, "OK token=%s user=%s %s\n", token, rfc_user, ag?ag:"group=");
812 #else
813  fprintf(stdout, "OK token=%s user=%s\n", token, rfc_user);
814 #endif
815  debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM, token, rfc_user);
816  if (log)
817  fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
818  PROGRAM, rfc_user);
819  goto cleanup;
820  } else {
821  if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1))
822  goto cleanup;
823  if (major_status & GSS_S_CONTINUE_NEEDED) {
824  debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
825  // XXX: where to get the server token for delivery to client? token is nullptr here.
826  fprintf(stdout, "ERR\n");
827  goto cleanup;
828  }
829  gss_release_buffer(&minor_status, &output_token);
830  major_status =
831  gss_display_name(&minor_status, client_name, &output_token,
832  nullptr);
833 
834  if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1))
835  goto cleanup;
836  /*
837  * Return dummy token AA. May need an extra return tag then AF
838  */
839  user = (char *) xmalloc(output_token.length + 1);
840  if (user == nullptr) {
841  debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
842  fprintf(stdout, "BH Not enough memory\n");
843  goto cleanup;
844  }
845  memcpy(user, output_token.value, output_token.length);
846  user[output_token.length] = '\0';
847  if (norealm && (p = strchr(user, '@')) != nullptr) {
848  *p = '\0';
849  }
850  rfc_user = rfc1738_escape(user);
851 #if HAVE_PAC_SUPPORT
852  fprintf(stdout, "OK token=%s user=%s %s\n", "AA==", rfc_user, ag?ag:"group=");
853 #else
854  fprintf(stdout, "OK token=%s user=%s\n", "AA==", rfc_user);
855 #endif
856  debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM, "AA==", rfc_user);
857  if (log)
858  fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
859  PROGRAM, rfc_user);
860  }
861 cleanup:
862  gss_release_buffer(&minor_status, &input_token);
863  gss_release_buffer(&minor_status, &output_token);
864  gss_release_cred(&minor_status, &server_creds);
865  if (server_name)
866  gss_release_name(&minor_status, &server_name);
867  if (client_name)
868  gss_release_name(&minor_status, &client_name);
869  if (kerberosToken) {
870  /* Allocated by parseNegTokenInit, but no matching free function exists.. */
871  if (!spnego_flag)
872  safe_free(kerberosToken);
873  }
874  if (spnego_flag) {
875  /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
876  safe_free(spnegoToken);
877  }
878  safe_free(token);
879  safe_free(user);
880  continue;
881  }
882  return EXIT_SUCCESS;
883 }
884 #else
885 #include <cstdlib>
886 #ifndef MAX_AUTHTOKEN_LEN
887 #define MAX_AUTHTOKEN_LEN 65535
888 #endif
889 int
890 main(int argc, char *const argv[])
891 {
892  setbuf(stdout, NULL);
893  setbuf(stdin, NULL);
894  char buf[MAX_AUTHTOKEN_LEN];
895  while (1) {
896  if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
897  fprintf(stdout, "BH input error\n");
898  exit(EXIT_SUCCESS);
899  }
900  fprintf(stdout, "BH Kerberos authentication not supported\n");
901  }
902  return EXIT_SUCCESS;
903 }
904 #endif /* HAVE_GSSAPI */
905 
void log(char *format,...)
const char * LogTime(void)
#define PROGRAM
Definition: support.h:189
void base64_encode_init(struct base64_encode_ctx *ctx)
Definition: base64.c:232
size_t base64_encode_update(struct base64_encode_ctx *ctx, char *dst, size_t length, const uint8_t *src)
Definition: base64.c:265
void base64_decode_init(struct base64_decode_ctx *ctx)
Definition: base64.c:54
size_t base64_encode_final(struct base64_encode_ctx *ctx, char *dst)
Definition: base64.c:308
#define base64_encode_len(length)
Definition: base64.h:169
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 getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:62
char * optarg
Definition: getopt.c:51
unsigned char code
Definition: html_quote.c:20
#define gss_nt_service_name
char * gethost_name(void)
#define SQUID_KERB_AUTH_VERSION
int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function, int log, int sout)
int check_k5_err(krb5_context context, const char *msg, krb5_error_code code)
static const unsigned char ntlmProtocol[]
#define MAX_AUTHTOKEN_LEN
int main(int argc, char *const argv[])
#define xfree
#define xstrdup
#define xmalloc
void freeaddrinfo()
int getaddrinfo()
const char * gai_strerror()
int getnameinfo()
#define rfc1738_escape(x)
Definition: rfc1738.h:52
#define MAXPATHLEN
Definition: stdio.h:62
char * strerror(int ern)
Definition: strerror.c:22
struct sockaddr * ai_addr
socklen_t ai_addrlen
struct addrinfo * ai_next
SBuf service_name(APP_SHORTNAME)
static int debug
Definition: tcp-banger3.c:105
#define NULL
Definition: types.h:166
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
#define safe_free(x)
Definition: xalloc.h:73

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors