2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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.
10 * -----------------------------------------------------------------------------
12 * Author: Markus Moeller (markus_moeller at compuserve.com)
14 * Copyright (C) 2007 Markus Moeller. All rights reserved.
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.
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.
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.
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.
35 * -----------------------------------------------------------------------------
39 #include "compat/getaddrinfo.h"
40 #include "compat/getnameinfo.h"
45 #include "negotiate_kerberos.h"
54 #if HAVE_KRB5_MEMORY_KEYTAB
55 typedef struct _krb5_kt_list
{
56 struct _krb5_kt_list
*next
;
57 krb5_keytab_entry
*entry
;
59 krb5_kt_list ktlist
= NULL
;
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
,
65 krb5_error_code
krb5_read_keytab(krb5_context context
,
67 krb5_kt_list
*kt_list
);
68 #endif /* HAVE_KRB5_MEMORY_KEYTAB */
70 #if HAVE_PAC_SUPPORT || HAVE_KRB5_MEMORY_KEYTAB
72 check_k5_err(krb5_context context
, const char *function
, krb5_error_code code
)
75 if (code
&& code
!= KRB5_KT_END
) {
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
);
96 * char hostname[sysconf(_SC_HOST_NAME_MAX)];
99 struct addrinfo
*hres
= NULL
, *hres_list
;
102 rc
= gethostname(hostname
, sizeof(hostname
)-1);
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
);
109 rc
= getaddrinfo(hostname
, NULL
, NULL
, &hres
);
111 debug((char *) "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
112 LogTime(), PROGRAM
, gai_strerror(rc
));
114 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
115 LogTime(), PROGRAM
, gai_strerror(rc
));
122 hres_list
= hres_list
->ai_next
;
124 rc
= getnameinfo(hres
->ai_addr
, hres
->ai_addrlen
, hostname
,
125 sizeof(hostname
), NULL
, 0, 0);
127 debug((char *) "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
128 LogTime(), PROGRAM
, gai_strerror(rc
));
130 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
131 LogTime(), PROGRAM
, gai_strerror(rc
));
136 hostname
[sizeof(hostname
)-1] = '\0';
137 return (xstrdup(hostname
));
141 check_gss_err(OM_uint32 major_status
, OM_uint32 minor_status
,
142 const char *function
, int log
, int sout
)
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
;
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
;
164 gss_release_buffer(&min_stat
, &status_string
);
166 if (sizeof(buf
) > len
+ 2) {
167 snprintf(buf
+ len
, (sizeof(buf
) - len
), "%s", ". ");
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
;
182 gss_release_buffer(&min_stat
, &status_string
);
184 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM
, function
, buf
);
186 fprintf(stdout
, "BH %s failed: %s\n", function
, buf
);
188 fprintf(stderr
, "%s| %s: INFO: User not authenticated\n", LogTime(),
195 #if HAVE_KRB5_MEMORY_KEYTAB
199 krb5_error_code
krb5_free_kt_list(krb5_context context
, krb5_kt_list list
)
201 krb5_kt_list lp
= list
;
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
);
207 krb5_error_code retval
= krb5_free_keytab_entry_contents(context
, lp
->entry
);
209 safe_free(lp
->entry
);
210 if (check_k5_err(context
, "krb5_kt_free_entry", retval
))
212 krb5_kt_list prev
= lp
;
219 * Read in a keytab and append it to list. If list starts as NULL,
220 * allocate a new one if necessary.
222 krb5_error_code
krb5_read_keytab(krb5_context context
, char *name
, krb5_kt_list
*list
)
224 krb5_kt_list lp
= NULL
, tail
= NULL
, back
= NULL
;
226 krb5_keytab_entry
*entry
;
227 krb5_kt_cursor cursor
;
228 krb5_error_code retval
= 0;
231 /* point lp at the tail of the list */
232 for (lp
= *list
; lp
->next
; lp
= lp
->next
);
235 retval
= krb5_kt_resolve(context
, name
, &kt
);
236 if (check_k5_err(context
, "krb5_kt_resolve", retval
))
238 retval
= krb5_kt_start_seq_get(context
, kt
, &cursor
);
239 if (check_k5_err(context
, "krb5_kt_start_seq_get", retval
))
242 entry
= (krb5_keytab_entry
*)xcalloc(1, sizeof (krb5_keytab_entry
));
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
));
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
))
256 if (!lp
) { /* if list is empty, start one */
257 lp
= (krb5_kt_list
)xmalloc(sizeof (*lp
));
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
));
267 lp
->next
= (krb5_kt_list
)xmalloc(sizeof (*lp
));
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
));
285 if (retval
== KRB5_KT_END
)
288 krb5_free_kt_list(context
, tail
);
296 krb5_kt_end_seq_get(context
, kt
, &cursor
);
298 krb5_kt_close(context
, kt
);
303 * Takes a kt_list and writes it to the named keytab.
305 krb5_error_code
krb5_write_keytab(krb5_context context
, krb5_kt_list list
, char *name
)
308 char ktname
[MAXPATHLEN
+sizeof("MEMORY:")+1];
309 krb5_error_code retval
= 0;
311 snprintf(ktname
, sizeof(ktname
), "%s", name
);
312 retval
= krb5_kt_resolve(context
, ktname
, &kt
);
315 for (krb5_kt_list lp
= list
; lp
; lp
= lp
->next
) {
316 retval
= krb5_kt_add_entry(context
, kt
, lp
->entry
);
321 * krb5_kt_close(context, kt);
325 #endif /* HAVE_KRB5_MEMORY_KEYTAB */
328 main(int argc
, char *const argv
[])
330 char buf
[MAX_AUTHTOKEN_LEN
];
333 char *rfc_user
= NULL
;
335 char ad_groups
[MAX_PAC_GROUP_SIZE
];
339 gss_buffer_desc data_set
= GSS_C_EMPTY_BUFFER
;
341 gss_buffer_desc type_id
= GSS_C_EMPTY_BUFFER
;
344 #if HAVE_PAC_SUPPORT || HAVE_KRB5_MEMORY_KEYTAB
345 krb5_context context
= NULL
;
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
;
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
;
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;
376 setbuf(stdout
, NULL
);
379 while (-1 != (opt
= getopt(argc
, argv
, "dirs:k:c:t:"))) {
396 keytab_name
= xstrdup(optarg
);
398 fprintf(stderr
, "ERROR: keytab file not given\n");
405 if ((ktp
=strchr(keytab_name
,':')))
409 if (stat((const char*)ktp
, &fstat
)) {
411 fprintf(stderr
, "ERROR: keytab file %s does not exist\n",keytab_name
);
413 fprintf(stderr
, "ERROR: Error %s during stat of keytab file %s\n",strerror(errno
),keytab_name
);
415 } else if (!S_ISREG(fstat
.st_mode
)) {
416 fprintf(stderr
, "ERROR: keytab file %s is not a file\n",keytab_name
);
421 if (access(ktp
, R_OK
)) {
422 fprintf(stderr
, "ERROR: keytab file %s is not accessible\n",keytab_name
);
432 rcache_dir
= xstrdup(optarg
);
434 fprintf(stderr
, "ERROR: replay cache directory not given\n");
441 if (stat((const char*)rcache_dir
, &dstat
)) {
443 fprintf(stderr
, "ERROR: replay cache directory %s does not exist\n",rcache_dir
);
445 fprintf(stderr
, "ERROR: Error %s during stat of replay cache directory %s\n",strerror(errno
),rcache_dir
);
447 } else if (!S_ISDIR(dstat
.st_mode
)) {
448 fprintf(stderr
, "ERROR: replay cache directory %s is not a directory\n",rcache_dir
);
453 if (access(rcache_dir
, W_OK
)) {
454 fprintf(stderr
, "ERROR: replay cache directory %s is not accessible\n",rcache_dir
);
461 rcache_type
= xstrdup(optarg
);
463 fprintf(stderr
, "ERROR: replay cache type not given\n");
469 service_principal
= xstrdup(optarg
);
471 fprintf(stderr
, "ERROR: service principal not given\n");
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");
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");
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
);
498 service
.value
= service_principal
;
499 service
.length
= strlen((char *) service
.value
);
501 host_name
= gethost_name();
504 "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
506 fprintf(stdout
, "BH hostname error\n");
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
);
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
);
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
);
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
);
540 keytab_name_env
= getenv("KRB5_KTNAME");
541 if (!keytab_name_env
)
542 keytab_name
= xstrdup("/etc/krb5.keytab");
544 keytab_name
= xstrdup(keytab_name_env
);
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
);
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
);
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
);
568 keytab_name
= xstrdup(memory_keytab_name
);
569 debug((char *) "%s| %s: INFO: Changed keytab to %s\n",
570 LogTime(), PROGRAM
, memory_keytab_name
);
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",
579 krb5_free_context(context
);
581 #ifdef HAVE_HEIMDAL_KERBEROS
582 gsskrb5_register_acceptor_identity(keytab_name
);
585 if (fgets(buf
, sizeof(buf
) - 1, stdin
) == NULL
) {
587 debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
588 LogTime(), PROGRAM
, ferror(stdin
),
589 strerror(ferror(stdin
)));
591 fprintf(stdout
, "BH input error\n");
592 exit(1); /* BIIG buffer */
594 fprintf(stdout
, "BH input error\n");
597 c
= (char *) memchr(buf
, '\n', sizeof(buf
) - 1);
605 debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM
);
606 fprintf(stdout
, "BH Oversized message\n");
610 debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", LogTime(), PROGRAM
, buf
, length
);
612 if (buf
[0] == '\0') {
613 debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM
);
614 fprintf(stdout
, "BH Invalid request\n");
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");
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
);
628 gss_release_name(&minor_status
, &server_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
);
634 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
636 xfree(kerberosToken
);
639 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
643 fprintf(stdout
, "BH quit command\n");
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");
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
;
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");
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
);
666 input_token
.length
= (size_t)base64_decode((char *) input_token
.value
, (unsigned int)input_token
.length
, buf
+3);
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",
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
));
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
);
685 server_name
= GSS_C_NO_NAME
;
686 major_status
= GSS_S_COMPLETE
;
690 major_status
= gss_import_name(&minor_status
, &service
,
691 gss_nt_service_name
, &server_name
);
694 if (check_gss_err(major_status
, minor_status
, "gss_import_name()", log
, 1))
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))
703 major_status
= gss_accept_sec_context(&minor_status
,
707 GSS_C_NO_CHANNEL_BINDINGS
,
708 &client_name
, NULL
, &output_token
, &ret_flags
, NULL
, NULL
);
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
));
715 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
716 fprintf(stdout
, "BH Not enough memory\n");
719 base64_encode_str(token
, base64_encode_len((int)spnegoTokenLength
),
720 (const char *) spnegoToken
, (int)spnegoTokenLength
);
722 if (check_gss_err(major_status
, minor_status
, "gss_accept_sec_context()", log
, 1))
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
);
729 gss_release_buffer(&minor_status
, &output_token
);
731 gss_display_name(&minor_status
, client_name
, &output_token
,
734 if (check_gss_err(major_status
, minor_status
, "gss_display_name()", log
, 1))
736 user
= (char *) xmalloc(output_token
.length
+ 1);
738 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
739 fprintf(stdout
, "BH Not enough memory\n");
742 memcpy(user
, output_token
.value
, output_token
.length
);
743 user
[output_token
.length
] = '\0';
744 if (norealm
&& (p
= strchr(user
, '@')) != NULL
) {
749 ret
= krb5_init_context(&context
);
750 if (!check_k5_err(context
, "krb5_init_context", ret
)) {
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
);
763 krb5_free_context(context
);
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
);
773 (void)gss_release_any_name_mapping(&minor_status
, client_name
, &type_id
, (gss_any_t
*)&pac
);
774 krb5_free_context(context
);
778 debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM
, ag
);
781 rfc_user
= rfc1738_escape(user
);
783 fprintf(stdout
, "AF %s %s %s\n", token
, rfc_user
, ag
?ag
:"group=");
785 fprintf(stdout
, "AF %s %s\n", token
, rfc_user
);
787 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM
, token
, rfc_user
);
789 fprintf(stderr
, "%s| %s: INFO: User %s authenticated\n", LogTime(),
793 if (check_gss_err(major_status
, minor_status
, "gss_accept_sec_context()", log
, 1))
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
);
800 gss_release_buffer(&minor_status
, &output_token
);
802 gss_display_name(&minor_status
, client_name
, &output_token
,
805 if (check_gss_err(major_status
, minor_status
, "gss_display_name()", log
, 1))
808 * Return dummy token AA. May need an extra return tag then AF
810 user
= (char *) xmalloc(output_token
.length
+ 1);
812 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
813 fprintf(stdout
, "BH Not enough memory\n");
816 memcpy(user
, output_token
.value
, output_token
.length
);
817 user
[output_token
.length
] = '\0';
818 if (norealm
&& (p
= strchr(user
, '@')) != NULL
) {
821 rfc_user
= rfc1738_escape(user
);
823 fprintf(stdout
, "AF %s %s %s\n", "AA==", rfc_user
, ag
?ag
:"group=");
825 fprintf(stdout
, "AF %s %s\n", "AA==", rfc_user
);
827 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM
, "AA==", rfc_user
);
829 fprintf(stderr
, "%s| %s: INFO: User %s authenticated\n", LogTime(),
833 gss_release_buffer(&minor_status
, &input_token
);
834 gss_release_buffer(&minor_status
, &output_token
);
835 gss_release_cred(&minor_status
, &server_creds
);
837 gss_release_name(&minor_status
, &server_name
);
839 gss_release_name(&minor_status
, &client_name
);
841 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
843 safe_free(kerberosToken
);
846 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
847 safe_free(spnegoToken
);
856 #ifndef MAX_AUTHTOKEN_LEN
857 #define MAX_AUTHTOKEN_LEN 65535
860 main(int argc
, char *const argv
[])
862 setbuf(stdout
, NULL
);
864 char buf
[MAX_AUTHTOKEN_LEN
];
866 if (fgets(buf
, sizeof(buf
) - 1, stdin
) == NULL
) {
867 fprintf(stdout
, "BH input error\n");
870 fprintf(stdout
, "BH Kerberos authentication not supported\n");
873 #endif /* HAVE_GSSAPI */