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