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