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 * -----------------------------------------------------------------------------
43 #include "negotiate_kerberos.h"
52 #if HAVE_KRB5_MEMORY_KEYTAB
53 typedef struct _krb5_kt_list
{
54 struct _krb5_kt_list
*next
;
55 krb5_keytab_entry
*entry
;
57 krb5_kt_list ktlist
= NULL
;
59 krb5_error_code
krb5_free_kt_list(krb5_context context
, krb5_kt_list kt_list
);
60 krb5_error_code
krb5_write_keytab(krb5_context context
,
63 krb5_error_code
krb5_read_keytab(krb5_context context
,
65 krb5_kt_list
*kt_list
);
66 #endif /* HAVE_KRB5_MEMORY_KEYTAB */
68 #if HAVE_PAC_SUPPORT || HAVE_KRB5_MEMORY_KEYTAB
70 check_k5_err(krb5_context context
, const char *function
, krb5_error_code code
)
73 if (code
&& code
!= KRB5_KT_END
) {
75 errmsg
= krb5_get_error_message(context
, code
);
76 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM
, function
, errmsg
);
77 fprintf(stderr
, "%s| %s: ERROR: %s: %s\n", LogTime(), PROGRAM
, function
, errmsg
);
78 #if HAVE_KRB5_FREE_ERROR_MESSAGE
79 krb5_free_error_message(context
, errmsg
);
80 #elif HAVE_KRB5_FREE_ERROR_STRING
81 krb5_free_error_string(context
, (char *)errmsg
);
94 * char hostname[sysconf(_SC_HOST_NAME_MAX)];
97 struct addrinfo
*hres
= NULL
, *hres_list
;
100 rc
= gethostname(hostname
, sizeof(hostname
)-1);
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
);
107 rc
= getaddrinfo(hostname
, NULL
, NULL
, &hres
);
109 debug((char *) "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
110 LogTime(), PROGRAM
, gai_strerror(rc
));
112 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
113 LogTime(), PROGRAM
, gai_strerror(rc
));
120 hres_list
= hres_list
->ai_next
;
122 rc
= getnameinfo(hres
->ai_addr
, hres
->ai_addrlen
, hostname
,
123 sizeof(hostname
), NULL
, 0, 0);
125 debug((char *) "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
126 LogTime(), PROGRAM
, gai_strerror(rc
));
128 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
129 LogTime(), PROGRAM
, gai_strerror(rc
));
134 hostname
[sizeof(hostname
)-1] = '\0';
135 return (xstrdup(hostname
));
139 check_gss_err(OM_uint32 major_status
, OM_uint32 minor_status
,
140 const char *function
, int log
, int sout
)
142 if (GSS_ERROR(major_status
)) {
143 OM_uint32 maj_stat
, min_stat
;
144 OM_uint32 msg_ctx
= 0;
145 gss_buffer_desc status_string
;
152 /* convert major status code (GSS-API error) to text */
153 maj_stat
= gss_display_status(&min_stat
, major_status
,
154 GSS_C_GSS_CODE
, GSS_C_NULL_OID
, &msg_ctx
, &status_string
);
155 if (maj_stat
== GSS_S_COMPLETE
&& status_string
.length
> 0) {
156 if (sizeof(buf
) > len
+ status_string
.length
+ 1) {
157 snprintf(buf
+ len
, (sizeof(buf
) - len
), "%s", (char *) status_string
.value
);
158 len
+= status_string
.length
;
162 gss_release_buffer(&min_stat
, &status_string
);
164 if (sizeof(buf
) > len
+ 2) {
165 snprintf(buf
+ len
, (sizeof(buf
) - len
), "%s", ". ");
170 /* convert minor status code (underlying routine error) to text */
171 maj_stat
= gss_display_status(&min_stat
, minor_status
,
172 GSS_C_MECH_CODE
, GSS_C_NULL_OID
, &msg_ctx
, &status_string
);
173 if (maj_stat
== GSS_S_COMPLETE
&& status_string
.length
> 0) {
174 if (sizeof(buf
) > len
+ status_string
.length
) {
175 snprintf(buf
+ len
, (sizeof(buf
) - len
), "%s", (char *) status_string
.value
);
176 len
+= status_string
.length
;
180 gss_release_buffer(&min_stat
, &status_string
);
182 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM
, function
, buf
);
184 fprintf(stdout
, "BH %s failed: %s\n", function
, buf
);
186 fprintf(stderr
, "%s| %s: INFO: User not authenticated\n", LogTime(),
193 #if HAVE_KRB5_MEMORY_KEYTAB
197 krb5_error_code
krb5_free_kt_list(krb5_context context
, krb5_kt_list list
)
199 krb5_kt_list lp
= list
;
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
);
205 krb5_error_code retval
= krb5_free_keytab_entry_contents(context
, lp
->entry
);
207 safe_free(lp
->entry
);
208 if (check_k5_err(context
, "krb5_kt_free_entry", retval
))
210 krb5_kt_list prev
= lp
;
217 * Read in a keytab and append it to list. If list starts as NULL,
218 * allocate a new one if necessary.
220 krb5_error_code
krb5_read_keytab(krb5_context context
, char *name
, krb5_kt_list
*list
)
222 krb5_kt_list lp
= NULL
, tail
= NULL
, back
= NULL
;
224 krb5_keytab_entry
*entry
;
225 krb5_kt_cursor cursor
;
226 krb5_error_code retval
= 0;
229 /* point lp at the tail of the list */
230 for (lp
= *list
; lp
->next
; lp
= lp
->next
);
233 retval
= krb5_kt_resolve(context
, name
, &kt
);
234 if (check_k5_err(context
, "krb5_kt_resolve", retval
))
236 retval
= krb5_kt_start_seq_get(context
, kt
, &cursor
);
237 if (check_k5_err(context
, "krb5_kt_start_seq_get", retval
))
240 entry
= (krb5_keytab_entry
*)xcalloc(1, sizeof (krb5_keytab_entry
));
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
));
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
))
254 if (!lp
) { /* if list is empty, start one */
255 lp
= (krb5_kt_list
)xmalloc(sizeof (*lp
));
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
));
265 lp
->next
= (krb5_kt_list
)xmalloc(sizeof (*lp
));
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
));
283 if (retval
== KRB5_KT_END
)
286 krb5_free_kt_list(context
, tail
);
294 krb5_kt_end_seq_get(context
, kt
, &cursor
);
296 krb5_kt_close(context
, kt
);
301 * Takes a kt_list and writes it to the named keytab.
303 krb5_error_code
krb5_write_keytab(krb5_context context
, krb5_kt_list list
, char *name
)
306 char ktname
[MAXPATHLEN
+sizeof("MEMORY:")+1];
307 krb5_error_code retval
= 0;
309 snprintf(ktname
, sizeof(ktname
), "%s", name
);
310 retval
= krb5_kt_resolve(context
, ktname
, &kt
);
313 for (krb5_kt_list lp
= list
; lp
; lp
= lp
->next
) {
314 retval
= krb5_kt_add_entry(context
, kt
, lp
->entry
);
319 * krb5_kt_close(context, kt);
323 #endif /* HAVE_KRB5_MEMORY_KEYTAB */
326 main(int argc
, char *const argv
[])
328 char buf
[MAX_AUTHTOKEN_LEN
];
331 char *rfc_user
= NULL
;
333 char ad_groups
[MAX_PAC_GROUP_SIZE
];
337 gss_buffer_desc data_set
= GSS_C_EMPTY_BUFFER
;
339 gss_buffer_desc type_id
= GSS_C_EMPTY_BUFFER
;
342 #if HAVE_PAC_SUPPORT || HAVE_KRB5_MEMORY_KEYTAB
343 krb5_context context
= NULL
;
348 int opt
, log
= 0, norealm
= 0;
349 OM_uint32 ret_flags
= 0, spnego_flag
= 0;
350 char *service_name
= (char *) "HTTP", *host_name
= NULL
;
352 char *service_principal
= NULL
;
353 char *keytab_name
= NULL
;
354 char *keytab_name_env
= NULL
;
355 #if HAVE_KRB5_MEMORY_KEYTAB
356 char *memory_keytab_name
= NULL
;
358 char *rcache_type
= NULL
;
359 char *rcache_type_env
= NULL
;
360 char *rcache_dir
= NULL
;
361 char *rcache_dir_env
= NULL
;
362 OM_uint32 major_status
, minor_status
;
363 gss_ctx_id_t gss_context
= GSS_C_NO_CONTEXT
;
364 gss_name_t client_name
= GSS_C_NO_NAME
;
365 gss_name_t server_name
= GSS_C_NO_NAME
;
366 gss_cred_id_t server_creds
= GSS_C_NO_CREDENTIAL
;
367 gss_buffer_desc service
= GSS_C_EMPTY_BUFFER
;
368 gss_buffer_desc input_token
= GSS_C_EMPTY_BUFFER
;
369 gss_buffer_desc output_token
= GSS_C_EMPTY_BUFFER
;
370 const unsigned char *kerberosToken
= NULL
;
371 const unsigned char *spnegoToken
= NULL
;
372 size_t spnegoTokenLength
= 0;
374 setbuf(stdout
, NULL
);
377 while (-1 != (opt
= getopt(argc
, argv
, "dirs:k:c:t:"))) {
394 keytab_name
= xstrdup(optarg
);
396 fprintf(stderr
, "ERROR: keytab file not given\n");
403 if ((ktp
=strchr(keytab_name
,':')))
407 if (stat((const char*)ktp
, &fstat
)) {
409 fprintf(stderr
, "ERROR: keytab file %s does not exist\n",keytab_name
);
411 fprintf(stderr
, "ERROR: Error %s during stat of keytab file %s\n",strerror(errno
),keytab_name
);
413 } else if (!S_ISREG(fstat
.st_mode
)) {
414 fprintf(stderr
, "ERROR: keytab file %s is not a file\n",keytab_name
);
419 if (access(ktp
, R_OK
)) {
420 fprintf(stderr
, "ERROR: keytab file %s is not accessible\n",keytab_name
);
430 rcache_dir
= xstrdup(optarg
);
432 fprintf(stderr
, "ERROR: replay cache directory not given\n");
439 if (stat((const char*)rcache_dir
, &dstat
)) {
441 fprintf(stderr
, "ERROR: replay cache directory %s does not exist\n",rcache_dir
);
443 fprintf(stderr
, "ERROR: Error %s during stat of replay cache directory %s\n",strerror(errno
),rcache_dir
);
445 } else if (!S_ISDIR(dstat
.st_mode
)) {
446 fprintf(stderr
, "ERROR: replay cache directory %s is not a directory\n",rcache_dir
);
451 if (access(rcache_dir
, W_OK
)) {
452 fprintf(stderr
, "ERROR: replay cache directory %s is not accessible\n",rcache_dir
);
459 rcache_type
= xstrdup(optarg
);
461 fprintf(stderr
, "ERROR: replay cache type not given\n");
467 service_principal
= xstrdup(optarg
);
469 fprintf(stderr
, "ERROR: service principal not given\n");
474 fprintf(stderr
, "Usage: \n");
475 fprintf(stderr
, "squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n");
476 fprintf(stderr
, "-d full debug\n");
477 fprintf(stderr
, "-i informational messages\n");
478 fprintf(stderr
, "-r remove realm from username\n");
479 fprintf(stderr
, "-s service principal name\n");
480 fprintf(stderr
, "-k keytab name\n");
481 fprintf(stderr
, "-c replay cache directory\n");
482 fprintf(stderr
, "-t replay cache type\n");
484 "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
485 fprintf(stderr
, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
490 debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM
, SQUID_KERB_AUTH_VERSION
);
491 if (service_principal
&& strcasecmp(service_principal
, "GSS_C_NO_NAME")) {
492 if (!strstr(service_principal
,"HTTP/")) {
493 debug((char *) "%s| %s: WARN: service_principal %s does not start with HTTP/\n",
494 LogTime(), PROGRAM
, service_principal
);
496 service
.value
= service_principal
;
497 service
.length
= strlen((char *) service
.value
);
499 host_name
= gethost_name();
502 "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
504 fprintf(stdout
, "BH hostname error\n");
507 service
.value
= xmalloc(strlen(service_name
) + strlen(host_name
) + 2);
508 snprintf((char *) service
.value
, strlen(service_name
) + strlen(host_name
) + 2,
509 "%s@%s", service_name
, host_name
);
510 service
.length
= strlen((char *) service
.value
);
515 rcache_type_env
= (char *) xmalloc(strlen("KRB5RCACHETYPE=")+strlen(rcache_type
)+1);
516 strcpy(rcache_type_env
, "KRB5RCACHETYPE=");
517 strcat(rcache_type_env
, rcache_type
);
518 putenv(rcache_type_env
);
519 debug((char *) "%s| %s: INFO: Setting replay cache type to %s\n",
520 LogTime(), PROGRAM
, rcache_type
);
524 rcache_dir_env
= (char *) xmalloc(strlen("KRB5RCACHEDIR=")+strlen(rcache_dir
)+1);
525 strcpy(rcache_dir_env
, "KRB5RCACHEDIR=");
526 strcat(rcache_dir_env
, rcache_dir
);
527 putenv(rcache_dir_env
);
528 debug((char *) "%s| %s: INFO: Setting replay cache directory to %s\n",
529 LogTime(), PROGRAM
, rcache_dir
);
533 keytab_name_env
= (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(keytab_name
)+1);
534 strcpy(keytab_name_env
, "KRB5_KTNAME=");
535 strcat(keytab_name_env
, keytab_name
);
536 putenv(keytab_name_env
);
538 keytab_name_env
= getenv("KRB5_KTNAME");
539 if (!keytab_name_env
)
540 keytab_name
= xstrdup("/etc/krb5.keytab");
542 keytab_name
= xstrdup(keytab_name_env
);
544 debug((char *) "%s| %s: INFO: Setting keytab to %s\n", LogTime(), PROGRAM
, keytab_name
);
545 #if HAVE_KRB5_MEMORY_KEYTAB
546 ret
= krb5_init_context(&context
);
547 if (!check_k5_err(context
, "krb5_init_context", ret
)) {
548 memory_keytab_name
= (char *)xmalloc(strlen("MEMORY:negotiate_kerberos_auth_")+16);
549 snprintf(memory_keytab_name
, strlen("MEMORY:negotiate_kerberos_auth_")+16,
550 "MEMORY:negotiate_kerberos_auth_%d", (unsigned int) getpid());
551 ret
= krb5_read_keytab(context
, keytab_name
, &ktlist
);
552 if (check_k5_err(context
, "krb5_read_keytab", ret
)) {
553 debug((char *) "%s| %s: ERROR: Reading keytab %s into list failed\n",
554 LogTime(), PROGRAM
, keytab_name
);
556 ret
= krb5_write_keytab(context
, ktlist
, memory_keytab_name
);
557 if (check_k5_err(context
, "krb5_write_keytab", ret
)) {
558 debug((char *) "%s| %s: ERROR: Writing list into keytab %s\n",
559 LogTime(), PROGRAM
, memory_keytab_name
);
561 keytab_name_env
= (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(memory_keytab_name
)+1);
562 strcpy(keytab_name_env
, "KRB5_KTNAME=");
563 strcat(keytab_name_env
, memory_keytab_name
);
564 putenv(keytab_name_env
);
566 keytab_name
= xstrdup(memory_keytab_name
);
567 debug((char *) "%s| %s: INFO: Changed keytab to %s\n",
568 LogTime(), PROGRAM
, memory_keytab_name
);
571 ret
= krb5_free_kt_list(context
,ktlist
);
572 if (check_k5_err(context
, "krb5_free_kt_list", ret
)) {
573 debug((char *) "%s| %s: ERROR: Freeing list failed\n",
577 krb5_free_context(context
);
579 #ifdef HAVE_HEIMDAL_KERBEROS
580 gsskrb5_register_acceptor_identity(keytab_name
);
583 if (fgets(buf
, sizeof(buf
) - 1, stdin
) == NULL
) {
585 debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
586 LogTime(), PROGRAM
, ferror(stdin
),
587 strerror(ferror(stdin
)));
589 fprintf(stdout
, "BH input error\n");
590 exit(1); /* BIIG buffer */
592 fprintf(stdout
, "BH input error\n");
595 c
= (char *) memchr(buf
, '\n', sizeof(buf
) - 1);
603 debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM
);
604 fprintf(stdout
, "BH Oversized message\n");
608 debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", LogTime(), PROGRAM
, buf
, length
);
610 if (buf
[0] == '\0') {
611 debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM
);
612 fprintf(stdout
, "BH Invalid request\n");
615 if (strlen(buf
) < 2) {
616 debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM
, buf
);
617 fprintf(stdout
, "BH Invalid request\n");
620 if (!strncmp(buf
, "QQ", 2)) {
621 gss_release_buffer(&minor_status
, &input_token
);
622 gss_release_buffer(&minor_status
, &output_token
);
623 gss_release_buffer(&minor_status
, &service
);
624 gss_release_cred(&minor_status
, &server_creds
);
626 gss_release_name(&minor_status
, &server_name
);
628 gss_release_name(&minor_status
, &client_name
);
629 if (gss_context
!= GSS_C_NO_CONTEXT
)
630 gss_delete_sec_context(&minor_status
, &gss_context
, NULL
);
632 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
634 xfree(kerberosToken
);
637 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
641 fprintf(stdout
, "BH quit command\n");
644 if (strncmp(buf
, "YR", 2) && strncmp(buf
, "KK", 2)) {
645 debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM
, buf
);
646 fprintf(stdout
, "BH Invalid request\n");
649 if (!strncmp(buf
, "YR", 2)) {
650 if (gss_context
!= GSS_C_NO_CONTEXT
)
651 gss_delete_sec_context(&minor_status
, &gss_context
, NULL
);
652 gss_context
= GSS_C_NO_CONTEXT
;
654 if (strlen(buf
) <= 3) {
655 debug((char *) "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM
, buf
);
656 fprintf(stdout
, "BH Invalid negotiate request\n");
659 const uint8_t *b64Token
= reinterpret_cast<const uint8_t*>(buf
+3);
660 const size_t srcLen
= strlen(buf
+3);
661 input_token
.length
= BASE64_DECODE_LENGTH(srcLen
);
662 debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n",
663 LogTime(), PROGRAM
, b64Token
, (int) input_token
.length
);
664 input_token
.value
= xmalloc(input_token
.length
);
666 struct base64_decode_ctx ctx
;
667 base64_decode_init(&ctx
);
669 if (!base64_decode_update(&ctx
, &dstLen
, static_cast<uint8_t*>(input_token
.value
), srcLen
, b64Token
) ||
670 !base64_decode_final(&ctx
)) {
671 debug((char *) "%s| %s: ERROR: Invalid base64 token [%s]\n", LogTime(), PROGRAM
, b64Token
);
672 fprintf(stdout
, "BH Invalid negotiate request token\n");
675 input_token
.length
= dstLen
;
677 if ((input_token
.length
>= sizeof ntlmProtocol
+ 1) &&
678 (!memcmp(input_token
.value
, ntlmProtocol
, sizeof ntlmProtocol
))) {
679 debug((char *) "%s| %s: WARNING: received type %d NTLM token\n",
681 (int) *((unsigned char *) input_token
.value
+
682 sizeof ntlmProtocol
));
683 fprintf(stdout
, "BH received type %d NTLM token\n",
684 (int) *((unsigned char *) input_token
.value
+
685 sizeof ntlmProtocol
));
688 if (service_principal
) {
689 if (strcasecmp(service_principal
, "GSS_C_NO_NAME")) {
690 major_status
= gss_import_name(&minor_status
, &service
,
691 (gss_OID
) GSS_C_NULL_OID
, &server_name
);
694 server_name
= GSS_C_NO_NAME
;
695 major_status
= GSS_S_COMPLETE
;
699 major_status
= gss_import_name(&minor_status
, &service
,
700 gss_nt_service_name
, &server_name
);
703 if (check_gss_err(major_status
, minor_status
, "gss_import_name()", log
, 1))
707 gss_acquire_cred(&minor_status
, server_name
, GSS_C_INDEFINITE
,
708 GSS_C_NO_OID_SET
, GSS_C_ACCEPT
, &server_creds
, NULL
, NULL
);
709 if (check_gss_err(major_status
, minor_status
, "gss_acquire_cred()", log
, 1))
712 major_status
= gss_accept_sec_context(&minor_status
,
716 GSS_C_NO_CHANNEL_BINDINGS
,
717 &client_name
, NULL
, &output_token
, &ret_flags
, NULL
, NULL
);
719 if (output_token
.length
) {
720 spnegoToken
= (const unsigned char *) output_token
.value
;
721 spnegoTokenLength
= output_token
.length
;
722 token
= (char *) xmalloc((size_t)base64_encode_len(spnegoTokenLength
));
724 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
725 fprintf(stdout
, "BH Not enough memory\n");
728 struct base64_encode_ctx tokCtx
;
729 base64_encode_init(&tokCtx
);
730 size_t blen
= base64_encode_update(&tokCtx
, reinterpret_cast<uint8_t*>(token
), spnegoTokenLength
, reinterpret_cast<const uint8_t*>(spnegoToken
));
731 blen
+= base64_encode_final(&tokCtx
, reinterpret_cast<uint8_t*>(token
)+blen
);
734 if (check_gss_err(major_status
, minor_status
, "gss_accept_sec_context()", log
, 1))
736 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
737 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM
);
738 fprintf(stdout
, "TT %s\n", token
);
741 gss_release_buffer(&minor_status
, &output_token
);
743 gss_display_name(&minor_status
, client_name
, &output_token
,
746 if (check_gss_err(major_status
, minor_status
, "gss_display_name()", log
, 1))
748 user
= (char *) xmalloc(output_token
.length
+ 1);
750 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
751 fprintf(stdout
, "BH Not enough memory\n");
754 memcpy(user
, output_token
.value
, output_token
.length
);
755 user
[output_token
.length
] = '\0';
756 if (norealm
&& (p
= strchr(user
, '@')) != NULL
) {
761 ret
= krb5_init_context(&context
);
762 if (!check_k5_err(context
, "krb5_init_context", ret
)) {
764 #define ADWIN2KPAC 128
765 major_status
= gsskrb5_extract_authz_data_from_sec_context(&minor_status
,
766 gss_context
, ADWIN2KPAC
, &data_set
);
767 if (!check_gss_err(major_status
, minor_status
,
768 "gsskrb5_extract_authz_data_from_sec_context()", log
, 0)) {
769 ret
= krb5_pac_parse(context
, data_set
.value
, data_set
.length
, &pac
);
770 gss_release_buffer(&minor_status
, &data_set
);
771 if (!check_k5_err(context
, "krb5_pac_parse", ret
)) {
772 ag
= get_ad_groups((char *)&ad_groups
, context
, pac
);
773 krb5_pac_free(context
, pac
);
775 krb5_free_context(context
);
778 type_id
.value
= (void *)"mspac";
779 type_id
.length
= strlen((char *)type_id
.value
);
780 #define KRB5PACLOGONINFO 1
781 major_status
= gss_map_name_to_any(&minor_status
, client_name
, KRB5PACLOGONINFO
, &type_id
, (gss_any_t
*)&pac
);
782 if (!check_gss_err(major_status
, minor_status
, "gss_map_name_to_any()", log
, 0)) {
783 ag
= get_ad_groups((char *)&ad_groups
,context
, pac
);
785 (void)gss_release_any_name_mapping(&minor_status
, client_name
, &type_id
, (gss_any_t
*)&pac
);
786 krb5_free_context(context
);
790 debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM
, ag
);
793 rfc_user
= rfc1738_escape(user
);
795 fprintf(stdout
, "AF %s %s %s\n", token
, rfc_user
, ag
?ag
:"group=");
797 fprintf(stdout
, "AF %s %s\n", token
, rfc_user
);
799 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM
, token
, rfc_user
);
801 fprintf(stderr
, "%s| %s: INFO: User %s authenticated\n", LogTime(),
805 if (check_gss_err(major_status
, minor_status
, "gss_accept_sec_context()", log
, 1))
807 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
808 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM
);
809 fprintf(stdout
, "NA %s\n", token
);
812 gss_release_buffer(&minor_status
, &output_token
);
814 gss_display_name(&minor_status
, client_name
, &output_token
,
817 if (check_gss_err(major_status
, minor_status
, "gss_display_name()", log
, 1))
820 * Return dummy token AA. May need an extra return tag then AF
822 user
= (char *) xmalloc(output_token
.length
+ 1);
824 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
825 fprintf(stdout
, "BH Not enough memory\n");
828 memcpy(user
, output_token
.value
, output_token
.length
);
829 user
[output_token
.length
] = '\0';
830 if (norealm
&& (p
= strchr(user
, '@')) != NULL
) {
833 rfc_user
= rfc1738_escape(user
);
835 fprintf(stdout
, "AF %s %s %s\n", "AA==", rfc_user
, ag
?ag
:"group=");
837 fprintf(stdout
, "AF %s %s\n", "AA==", rfc_user
);
839 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM
, "AA==", rfc_user
);
841 fprintf(stderr
, "%s| %s: INFO: User %s authenticated\n", LogTime(),
845 gss_release_buffer(&minor_status
, &input_token
);
846 gss_release_buffer(&minor_status
, &output_token
);
847 gss_release_cred(&minor_status
, &server_creds
);
849 gss_release_name(&minor_status
, &server_name
);
851 gss_release_name(&minor_status
, &client_name
);
853 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
855 safe_free(kerberosToken
);
858 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
859 safe_free(spnegoToken
);
868 #ifndef MAX_AUTHTOKEN_LEN
869 #define MAX_AUTHTOKEN_LEN 65535
872 main(int argc
, char *const argv
[])
874 setbuf(stdout
, NULL
);
876 char buf
[MAX_AUTHTOKEN_LEN
];
878 if (fgets(buf
, sizeof(buf
) - 1, stdin
) == NULL
) {
879 fprintf(stdout
, "BH input error\n");
882 fprintf(stdout
, "BH Kerberos authentication not supported\n");
885 #endif /* HAVE_GSSAPI */