]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/negotiate/kerberos/negotiate_kerberos_auth.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / auth / negotiate / kerberos / negotiate_kerberos_auth.cc
CommitLineData
ca02e0ec 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
ca02e0ec
AJ
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
3e5d7cdf 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 *
ba4fe07c
AJ
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 *
3e5d7cdf 35 * -----------------------------------------------------------------------------
36 */
ca02e0ec 37
f7f3304a 38#include "squid.h"
602d9612 39#include "rfc1738.h"
d1f95b42 40
3ad12bda 41#if HAVE_GSSAPI
e9658d82 42
4ebcf1ce 43#include "negotiate_kerberos.h"
3e5d7cdf 44
685277d8
MM
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 = NULL;
58
c8df5896 59krb5_keytab memory_keytab;
60
685277d8
MM
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
685277d8
MM
70int
71check_k5_err(krb5_context context, const char *function, krb5_error_code code)
72{
73
1aa0fb05 74 if (code && code != KRB5_KT_END) {
685277d8
MM
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}
685277d8 89
3ad12bda
AJ
90char *
91gethost_name(void)
26ac0430 92{
e6aa9181 93 /*
b1218840
AJ
94 * char hostname[sysconf(_SC_HOST_NAME_MAX)];
95 */
3d62cc61 96 char hostname[1024];
3ad12bda
AJ
97 struct addrinfo *hres = NULL, *hres_list;
98 int rc, count;
26ac0430 99
bc30ad64 100 rc = gethostname(hostname, sizeof(hostname)-1);
26ac0430 101 if (rc) {
685277d8 102 debug((char *) "%s| %s: ERROR: resolving hostname '%s' failed\n", LogTime(), PROGRAM, hostname);
2e881a6f
A
103 fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
104 LogTime(), PROGRAM, hostname);
105 return NULL;
26ac0430 106 }
27bc2077 107 rc = getaddrinfo(hostname, NULL, NULL, &hres);
4616aac3 108 if (rc != 0 || hres == NULL ) {
685277d8
MM
109 debug((char *) "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
110 LogTime(), PROGRAM, gai_strerror(rc));
2e881a6f
A
111 fprintf(stderr,
112 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
113 LogTime(), PROGRAM, gai_strerror(rc));
114 return NULL;
26ac0430 115 }
3ad12bda
AJ
116 hres_list = hres;
117 count = 0;
26ac0430 118 while (hres_list) {
755494da 119 ++count;
2e881a6f 120 hres_list = hres_list->ai_next;
26ac0430 121 }
27bc2077 122 rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
2e881a6f 123 sizeof(hostname), NULL, 0, 0);
26ac0430 124 if (rc != 0) {
685277d8
MM
125 debug((char *) "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
126 LogTime(), PROGRAM, gai_strerror(rc));
2e881a6f
A
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 NULL;
3e5d7cdf 132 }
27bc2077 133 freeaddrinfo(hres);
bc30ad64 134 hostname[sizeof(hostname)-1] = '\0';
3ad12bda 135 return (xstrdup(hostname));
3e5d7cdf 136}
137
3ad12bda
AJ
138int
139check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
4ebcf1ce 140 const char *function, int log, int sout)
26ac0430
AJ
141{
142 if (GSS_ERROR(major_status)) {
2e881a6f
A
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;
08885c7f 151 do {
2e881a6f
A
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);
08885c7f 155 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
2e881a6f
A
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 }
08885c7f
MM
160 } else
161 msg_ctx = 0;
2e881a6f 162 gss_release_buffer(&min_stat, &status_string);
08885c7f 163 } while (msg_ctx);
2e881a6f
A
164 if (sizeof(buf) > len + 2) {
165 snprintf(buf + len, (sizeof(buf) - len), "%s", ". ");
166 len += 2;
167 }
168 msg_ctx = 0;
08885c7f 169 do {
2e881a6f
A
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);
08885c7f 173 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
2e881a6f
A
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 }
08885c7f
MM
178 } else
179 msg_ctx = 0;
2e881a6f 180 gss_release_buffer(&min_stat, &status_string);
08885c7f 181 } while (msg_ctx);
2e881a6f 182 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf);
4ebcf1ce
MM
183 if (sout)
184 fprintf(stdout, "BH %s failed: %s\n", function, buf);
2e881a6f
A
185 if (log)
186 fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(),
187 PROGRAM);
188 return (1);
3e5d7cdf 189 }
3ad12bda 190 return (0);
3e5d7cdf 191}
192
685277d8
MM
193#if HAVE_KRB5_MEMORY_KEYTAB
194/*
195 * Free a kt_list
196 */
197krb5_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 */
220krb5_error_code krb5_read_keytab(krb5_context context, char *name, krb5_kt_list *list)
221{
222 krb5_kt_list lp = NULL, tail = NULL, back = NULL;
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 = NULL;
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 = NULL;
288 if (back)
289 back->next = NULL;
290 }
291 }
292 if (!*list)
293 *list = tail;
294 krb5_kt_end_seq_get(context, kt, &cursor);
295close_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 */
303krb5_error_code krb5_write_keytab(krb5_context context, krb5_kt_list list, char *name)
304{
685277d8
MM
305 char ktname[MAXPATHLEN+sizeof("MEMORY:")+1];
306 krb5_error_code retval = 0;
307
308 snprintf(ktname, sizeof(ktname), "%s", name);
c8df5896 309 retval = krb5_kt_resolve(context, ktname, &memory_keytab);
685277d8
MM
310 if (retval)
311 return retval;
312 for (krb5_kt_list lp = list; lp; lp = lp->next) {
c8df5896 313 retval = krb5_kt_add_entry(context, memory_keytab, lp->entry);
685277d8
MM
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
3ad12bda
AJ
324int
325main(int argc, char *const argv[])
3e5d7cdf 326{
26ac0430 327 char buf[MAX_AUTHTOKEN_LEN];
3ad12bda
AJ
328 char *c, *p;
329 char *user = NULL;
4ebcf1ce
MM
330 char *rfc_user = NULL;
331#if HAVE_PAC_SUPPORT
332 char ad_groups[MAX_PAC_GROUP_SIZE];
333 char *ag=NULL;
4ebcf1ce 334 krb5_pac pac;
1a22a39e 335#if USE_HEIMDAL_KRB5
4ebcf1ce
MM
336 gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
337#else
338 gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
339#endif
685277d8 340#endif
685277d8
MM
341 krb5_context context = NULL;
342 krb5_error_code ret;
4ebcf1ce 343 long length = 0;
3ad12bda 344 static int err = 0;
e673ba3a 345 int opt, log = 0, norealm = 0;
3ad12bda
AJ
346 OM_uint32 ret_flags = 0, spnego_flag = 0;
347 char *service_name = (char *) "HTTP", *host_name = NULL;
26ac0430
AJ
348 char *token = NULL;
349 char *service_principal = NULL;
685277d8
MM
350 char *keytab_name = NULL;
351 char *keytab_name_env = NULL;
4616aac3 352 char default_keytab[MAXPATHLEN];
685277d8
MM
353#if HAVE_KRB5_MEMORY_KEYTAB
354 char *memory_keytab_name = NULL;
c8df5896 355 char *memory_keytab_name_env = NULL;
685277d8
MM
356#endif
357 char *rcache_type = NULL;
358 char *rcache_type_env = NULL;
359 char *rcache_dir = NULL;
360 char *rcache_dir_env = NULL;
26ac0430 361 OM_uint32 major_status, minor_status;
3ad12bda
AJ
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 = NULL;
3ad12bda
AJ
370 const unsigned char *spnegoToken = NULL;
371 size_t spnegoTokenLength = 0;
26ac0430 372
3ad12bda
AJ
373 setbuf(stdout, NULL);
374 setbuf(stdin, NULL);
26ac0430 375
685277d8 376 while (-1 != (opt = getopt(argc, argv, "dirs:k:c:t:"))) {
2e881a6f
A
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;
685277d8
MM
387 case 'k':
388#if HAVE_SYS_STAT_H
389 struct stat fstat;
390 char *ktp;
391#endif
f53969cc 392 if (optarg)
685277d8 393 keytab_name = xstrdup(optarg);
f53969cc 394 else {
685277d8 395 fprintf(stderr, "ERROR: keytab file not given\n");
24885773 396 exit(EXIT_FAILURE);
f53969cc 397 }
685277d8
MM
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);
24885773 411 exit(EXIT_FAILURE);
685277d8
MM
412 } else if (!S_ISREG(fstat.st_mode)) {
413 fprintf(stderr, "ERROR: keytab file %s is not a file\n",keytab_name);
24885773 414 exit(EXIT_FAILURE);
685277d8
MM
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);
24885773 420 exit(EXIT_FAILURE);
685277d8
MM
421 }
422#endif
423 break;
424 case 'c':
425#if HAVE_SYS_STAT_H
426 struct stat dstat;
427#endif
f53969cc 428 if (optarg)
685277d8 429 rcache_dir = xstrdup(optarg);
f53969cc 430 else {
685277d8 431 fprintf(stderr, "ERROR: replay cache directory not given\n");
24885773 432 exit(EXIT_FAILURE);
f53969cc 433 }
685277d8
MM
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);
24885773 443 exit(EXIT_FAILURE);
685277d8
MM
444 } else if (!S_ISDIR(dstat.st_mode)) {
445 fprintf(stderr, "ERROR: replay cache directory %s is not a directory\n",rcache_dir);
24885773 446 exit(EXIT_FAILURE);
685277d8
MM
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);
24885773 452 exit(EXIT_FAILURE);
685277d8
MM
453 }
454#endif
455 break;
456 case 't':
f53969cc 457 if (optarg)
685277d8 458 rcache_type = xstrdup(optarg);
f53969cc 459 else {
685277d8 460 fprintf(stderr, "ERROR: replay cache type not given\n");
24885773 461 exit(EXIT_FAILURE);
f53969cc 462 }
685277d8 463 break;
2e881a6f 464 case 's':
f53969cc 465 if (optarg)
685277d8 466 service_principal = xstrdup(optarg);
f53969cc 467 else {
685277d8 468 fprintf(stderr, "ERROR: service principal not given\n");
24885773 469 exit(EXIT_FAILURE);
f53969cc 470 }
2e881a6f 471 break;
685277d8 472 default:
2e881a6f 473 fprintf(stderr, "Usage: \n");
685277d8 474 fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n");
2e881a6f
A
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");
685277d8
MM
479 fprintf(stderr, "-k keytab name\n");
480 fprintf(stderr, "-c replay cache directory\n");
481 fprintf(stderr, "-t replay cache type\n");
2e881a6f
A
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");
24885773 485 exit(EXIT_SUCCESS);
2e881a6f 486 }
3e5d7cdf 487 }
488
b1218840 489 debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION);
3ad12bda 490 if (service_principal && strcasecmp(service_principal, "GSS_C_NO_NAME")) {
685277d8
MM
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 }
2e881a6f
A
495 service.value = service_principal;
496 service.length = strlen((char *) service.value);
26ac0430 497 } else {
2e881a6f
A
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");
24885773 504 exit(EXIT_FAILURE);
2e881a6f
A
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);
4ebcf1ce 510 xfree(host_name);
3e5d7cdf 511 }
512
685277d8
MM
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");
4616aac3
MM
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 }
39c178ae 543 keytab_name = xstrdup(default_keytab);
4616aac3
MM
544 krb5_free_context(context);
545 } else
685277d8
MM
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 {
c8df5896 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);
685277d8
MM
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 }
1aa0fb05
MM
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 }
685277d8
MM
580 }
581 krb5_free_context(context);
582#endif
583#ifdef HAVE_HEIMDAL_KERBEROS
584 gsskrb5_register_acceptor_identity(keytab_name);
585#endif
26ac0430 586 while (1) {
2e881a6f
A
587 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
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");
24885773 594 exit(EXIT_FAILURE); /* BIIG buffer */
2e881a6f
A
595 }
596 fprintf(stdout, "BH input error\n");
24885773 597 exit(EXIT_SUCCESS);
2e881a6f
A
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 }
4ebcf1ce 612 debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", LogTime(), PROGRAM, buf, length);
2e881a6f
A
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, NULL);
635 if (kerberosToken) {
636 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
637 if (!spnego_flag)
4ebcf1ce 638 xfree(kerberosToken);
2e881a6f
A
639 }
640 if (spnego_flag) {
641 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
4ebcf1ce 642 xfree(spnegoToken);
2e881a6f 643 }
4ebcf1ce 644 xfree(token);
c8df5896 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);
2e881a6f 657 fprintf(stdout, "BH quit command\n");
24885773 658 exit(EXIT_SUCCESS);
2e881a6f
A
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, NULL);
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 }
1d11e9b3 675 const char *b64Token = buf+3;
40f220c3 676 const size_t srcLen = strlen(buf+3);
18db38a8
MM
677 input_token.length = BASE64_DECODE_LENGTH(srcLen);
678 debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n",
aadbbd7d 679 LogTime(), PROGRAM, b64Token, (int) input_token.length);
2e881a6f
A
680 input_token.value = xmalloc(input_token.length);
681
aadbbd7d
AJ
682 struct base64_decode_ctx ctx;
683 base64_decode_init(&ctx);
684 size_t dstLen = 0;
18db38a8 685 if (!base64_decode_update(&ctx, &dstLen, static_cast<uint8_t*>(input_token.value), srcLen, b64Token) ||
aadbbd7d
AJ
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;
2e881a6f
A
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;
4ebcf1ce 712 minor_status = 0;
2e881a6f
A
713 }
714 } else {
715 major_status = gss_import_name(&minor_status, &service,
716 gss_nt_service_name, &server_name);
717 }
718
4ebcf1ce 719 if (check_gss_err(major_status, minor_status, "gss_import_name()", log, 1))
2e881a6f
A
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, NULL, NULL);
4ebcf1ce 725 if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log, 1))
2e881a6f
A
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, NULL, &output_token, &ret_flags, NULL, NULL);
734
2e881a6f
A
735 if (output_token.length) {
736 spnegoToken = (const unsigned char *) output_token.value;
737 spnegoTokenLength = output_token.length;
aadbbd7d 738 token = (char *) xmalloc((size_t)base64_encode_len(spnegoTokenLength));
2e881a6f
A
739 if (token == NULL) {
740 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
741 fprintf(stdout, "BH Not enough memory\n");
742 goto cleanup;
743 }
aadbbd7d
AJ
744 struct base64_encode_ctx tokCtx;
745 base64_encode_init(&tokCtx);
1d11e9b3
AJ
746 size_t blen = base64_encode_update(&tokCtx, token, spnegoTokenLength, reinterpret_cast<const uint8_t*>(spnegoToken));
747 blen += base64_encode_final(&tokCtx, token+blen);
aadbbd7d 748 token[blen] = '\0';
2e881a6f 749
4ebcf1ce 750 if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1))
2e881a6f
A
751 goto cleanup;
752 if (major_status & GSS_S_CONTINUE_NEEDED) {
753 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
1c19010b 754 fprintf(stdout, "TT token=%s\n", token);
2e881a6f
A
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 NULL);
761
4ebcf1ce 762 if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1))
2e881a6f
A
763 goto cleanup;
764 user = (char *) xmalloc(output_token.length + 1);
765 if (user == NULL) {
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, '@')) != NULL) {
773 *p = '\0';
774 }
4ebcf1ce
MM
775
776#if HAVE_PAC_SUPPORT
777 ret = krb5_init_context(&context);
778 if (!check_k5_err(context, "krb5_init_context", ret)) {
1a22a39e 779#if USE_HEIMDAL_KRB5
4ebcf1ce
MM
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
4ebcf1ce 809 rfc_user = rfc1738_escape(user);
2eb6054f 810#if HAVE_PAC_SUPPORT
1c19010b 811 fprintf(stdout, "OK token=%s user=%s %s\n", token, rfc_user, ag?ag:"group=");
2eb6054f 812#else
1c19010b 813 fprintf(stdout, "OK token=%s user=%s\n", token, rfc_user);
2eb6054f 814#endif
1c19010b 815 debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM, token, rfc_user);
2e881a6f
A
816 if (log)
817 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
2eb6054f 818 PROGRAM, rfc_user);
2e881a6f
A
819 goto cleanup;
820 } else {
4ebcf1ce 821 if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log, 1))
2e881a6f
A
822 goto cleanup;
823 if (major_status & GSS_S_CONTINUE_NEEDED) {
824 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
4f3c41b3
AJ
825 // XXX: where to get the server token for delivery to client? token is nullptr here.
826 fprintf(stdout, "ERR\n");
2e881a6f
A
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 NULL);
833
4ebcf1ce 834 if (check_gss_err(major_status, minor_status, "gss_display_name()", log, 1))
2e881a6f
A
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 == NULL) {
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, '@')) != NULL) {
848 *p = '\0';
849 }
2eb6054f
MM
850 rfc_user = rfc1738_escape(user);
851#if HAVE_PAC_SUPPORT
1c19010b 852 fprintf(stdout, "OK token=%s user=%s %s\n", "AA==", rfc_user, ag?ag:"group=");
2eb6054f 853#else
1c19010b 854 fprintf(stdout, "OK token=%s user=%s\n", "AA==", rfc_user);
2eb6054f 855#endif
1c19010b 856 debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM, "AA==", rfc_user);
2e881a6f
A
857 if (log)
858 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
2eb6054f 859 PROGRAM, rfc_user);
2e881a6f
A
860 }
861cleanup:
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)
4ebcf1ce 872 safe_free(kerberosToken);
2e881a6f
A
873 }
874 if (spnego_flag) {
875 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
4ebcf1ce 876 safe_free(spnegoToken);
2e881a6f 877 }
4ebcf1ce
MM
878 safe_free(token);
879 safe_free(user);
2e881a6f 880 continue;
3e5d7cdf 881 }
24885773 882 return EXIT_SUCCESS;
3e5d7cdf 883}
6989c6dc 884#else
074d6a40 885#include <cstdlib>
6989c6dc
AJ
886#ifndef MAX_AUTHTOKEN_LEN
887#define MAX_AUTHTOKEN_LEN 65535
888#endif
889int
890main(int argc, char *const argv[])
891{
892 setbuf(stdout, NULL);
893 setbuf(stdin, NULL);
894 char buf[MAX_AUTHTOKEN_LEN];
895 while (1) {
2e881a6f
A
896 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
897 fprintf(stdout, "BH input error\n");
24885773 898 exit(EXIT_SUCCESS);
2e881a6f
A
899 }
900 fprintf(stdout, "BH Kerberos authentication not supported\n");
6989c6dc 901 }
24885773 902 return EXIT_SUCCESS;
6989c6dc 903}
3ad12bda 904#endif /* HAVE_GSSAPI */
f53969cc 905