2 * Copyright (C) 1996-2022 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_keytab memory_keytab
;
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 */
71 check_k5_err(krb5_context context
, const char *function
, krb5_error_code code
)
74 if (code
&& code
!= KRB5_KT_END
) {
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
);
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
);
108 if (rc
!= 0 || hres
== NULL
) {
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
)
305 char ktname
[MAXPATHLEN
+sizeof("MEMORY:")+1];
306 krb5_error_code retval
= 0;
308 snprintf(ktname
, sizeof(ktname
), "%s", name
);
309 retval
= krb5_kt_resolve(context
, ktname
, &memory_keytab
);
312 for (krb5_kt_list lp
= list
; lp
; lp
= lp
->next
) {
313 retval
= krb5_kt_add_entry(context
, memory_keytab
, lp
->entry
);
318 * krb5_kt_close(context, kt);
322 #endif /* HAVE_KRB5_MEMORY_KEYTAB */
325 main(int argc
, char *const argv
[])
327 char buf
[MAX_AUTHTOKEN_LEN
];
330 char *rfc_user
= NULL
;
332 char ad_groups
[MAX_PAC_GROUP_SIZE
];
336 gss_buffer_desc data_set
= GSS_C_EMPTY_BUFFER
;
338 gss_buffer_desc type_id
= GSS_C_EMPTY_BUFFER
;
341 krb5_context context
= NULL
;
345 int opt
, log
= 0, norealm
= 0;
346 OM_uint32 ret_flags
= 0, spnego_flag
= 0;
347 char *service_name
= (char *) "HTTP", *host_name
= NULL
;
349 char *service_principal
= NULL
;
350 char *keytab_name
= NULL
;
351 char *keytab_name_env
= NULL
;
352 char default_keytab
[MAXPATHLEN
];
353 #if HAVE_KRB5_MEMORY_KEYTAB
354 char *memory_keytab_name
= NULL
;
355 char *memory_keytab_name_env
= NULL
;
357 char *rcache_type
= NULL
;
358 char *rcache_type_env
= NULL
;
359 char *rcache_dir
= NULL
;
360 char *rcache_dir_env
= NULL
;
361 OM_uint32 major_status
, minor_status
;
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
;
370 const unsigned char *spnegoToken
= NULL
;
371 size_t spnegoTokenLength
= 0;
373 setbuf(stdout
, NULL
);
376 while (-1 != (opt
= getopt(argc
, argv
, "dirs:k:c:t:"))) {
393 keytab_name
= xstrdup(optarg
);
395 fprintf(stderr
, "ERROR: keytab file not given\n");
402 if ((ktp
=strchr(keytab_name
,':')))
406 if (stat((const char*)ktp
, &fstat
)) {
408 fprintf(stderr
, "ERROR: keytab file %s does not exist\n",keytab_name
);
410 fprintf(stderr
, "ERROR: Error %s during stat of keytab file %s\n",strerror(errno
),keytab_name
);
412 } else if (!S_ISREG(fstat
.st_mode
)) {
413 fprintf(stderr
, "ERROR: keytab file %s is not a file\n",keytab_name
);
418 if (access(ktp
, R_OK
)) {
419 fprintf(stderr
, "ERROR: keytab file %s is not accessible\n",keytab_name
);
429 rcache_dir
= xstrdup(optarg
);
431 fprintf(stderr
, "ERROR: replay cache directory not given\n");
438 if (stat((const char*)rcache_dir
, &dstat
)) {
440 fprintf(stderr
, "ERROR: replay cache directory %s does not exist\n",rcache_dir
);
442 fprintf(stderr
, "ERROR: Error %s during stat of replay cache directory %s\n",strerror(errno
),rcache_dir
);
444 } else if (!S_ISDIR(dstat
.st_mode
)) {
445 fprintf(stderr
, "ERROR: replay cache directory %s is not a directory\n",rcache_dir
);
450 if (access(rcache_dir
, W_OK
)) {
451 fprintf(stderr
, "ERROR: replay cache directory %s is not accessible\n",rcache_dir
);
458 rcache_type
= xstrdup(optarg
);
460 fprintf(stderr
, "ERROR: replay cache type not given\n");
466 service_principal
= xstrdup(optarg
);
468 fprintf(stderr
, "ERROR: service principal not given\n");
473 fprintf(stderr
, "Usage: \n");
474 fprintf(stderr
, "squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n");
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");
479 fprintf(stderr
, "-k keytab name\n");
480 fprintf(stderr
, "-c replay cache directory\n");
481 fprintf(stderr
, "-t replay cache type\n");
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");
489 debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM
, SQUID_KERB_AUTH_VERSION
);
490 if (service_principal
&& strcasecmp(service_principal
, "GSS_C_NO_NAME")) {
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
);
495 service
.value
= service_principal
;
496 service
.length
= strlen((char *) service
.value
);
498 host_name
= gethost_name();
501 "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
503 fprintf(stdout
, "BH hostname error\n");
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
);
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
);
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
);
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
);
537 keytab_name_env
= getenv("KRB5_KTNAME");
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
);
543 keytab_name
= xstrdup(default_keytab
);
544 krb5_free_context(context
);
546 keytab_name
= xstrdup(keytab_name_env
);
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
);
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
);
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
);
570 keytab_name
= xstrdup(memory_keytab_name
);
571 debug((char *) "%s| %s: INFO: Changed keytab to %s\n",
572 LogTime(), PROGRAM
, memory_keytab_name
);
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",
581 krb5_free_context(context
);
583 #ifdef HAVE_HEIMDAL_KERBEROS
584 gsskrb5_register_acceptor_identity(keytab_name
);
587 if (fgets(buf
, sizeof(buf
) - 1, stdin
) == NULL
) {
589 debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
590 LogTime(), PROGRAM
, ferror(stdin
),
591 strerror(ferror(stdin
)));
593 fprintf(stdout
, "BH input error\n");
594 exit(EXIT_FAILURE
); /* BIIG buffer */
596 fprintf(stdout
, "BH input error\n");
599 c
= (char *) memchr(buf
, '\n', sizeof(buf
) - 1);
607 debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM
);
608 fprintf(stdout
, "BH Oversized message\n");
612 debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n", LogTime(), PROGRAM
, buf
, length
);
614 if (buf
[0] == '\0') {
615 debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM
);
616 fprintf(stdout
, "BH Invalid request\n");
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");
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
);
630 gss_release_name(&minor_status
, &server_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
);
636 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
638 xfree(kerberosToken
);
641 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
646 xfree(rcache_type_env
);
648 xfree(rcache_dir_env
);
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
);
657 fprintf(stdout
, "BH quit command\n");
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");
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
;
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");
675 const char *b64Token
= buf
+3;
676 const size_t srcLen
= strlen(buf
+3);
677 input_token
.length
= BASE64_DECODE_LENGTH(srcLen
);
678 debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n",
679 LogTime(), PROGRAM
, b64Token
, (int) input_token
.length
);
680 input_token
.value
= xmalloc(input_token
.length
);
682 struct base64_decode_ctx ctx
;
683 base64_decode_init(&ctx
);
685 if (!base64_decode_update(&ctx
, &dstLen
, static_cast<uint8_t*>(input_token
.value
), srcLen
, b64Token
) ||
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");
691 input_token
.length
= dstLen
;
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",
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
));
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
);
710 server_name
= GSS_C_NO_NAME
;
711 major_status
= GSS_S_COMPLETE
;
715 major_status
= gss_import_name(&minor_status
, &service
,
716 gss_nt_service_name
, &server_name
);
719 if (check_gss_err(major_status
, minor_status
, "gss_import_name()", log
, 1))
723 gss_acquire_cred(&minor_status
, server_name
, GSS_C_INDEFINITE
,
724 GSS_C_NO_OID_SET
, GSS_C_ACCEPT
, &server_creds
, NULL
, NULL
);
725 if (check_gss_err(major_status
, minor_status
, "gss_acquire_cred()", log
, 1))
728 major_status
= gss_accept_sec_context(&minor_status
,
732 GSS_C_NO_CHANNEL_BINDINGS
,
733 &client_name
, NULL
, &output_token
, &ret_flags
, NULL
, NULL
);
735 if (output_token
.length
) {
736 spnegoToken
= (const unsigned char *) output_token
.value
;
737 spnegoTokenLength
= output_token
.length
;
738 token
= (char *) xmalloc((size_t)base64_encode_len(spnegoTokenLength
));
740 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
741 fprintf(stdout
, "BH Not enough memory\n");
744 struct base64_encode_ctx tokCtx
;
745 base64_encode_init(&tokCtx
);
746 size_t blen
= base64_encode_update(&tokCtx
, token
, spnegoTokenLength
, reinterpret_cast<const uint8_t*>(spnegoToken
));
747 blen
+= base64_encode_final(&tokCtx
, token
+blen
);
750 if (check_gss_err(major_status
, minor_status
, "gss_accept_sec_context()", log
, 1))
752 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
753 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM
);
754 fprintf(stdout
, "TT token=%s\n", token
);
757 gss_release_buffer(&minor_status
, &output_token
);
759 gss_display_name(&minor_status
, client_name
, &output_token
,
762 if (check_gss_err(major_status
, minor_status
, "gss_display_name()", log
, 1))
764 user
= (char *) xmalloc(output_token
.length
+ 1);
766 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
767 fprintf(stdout
, "BH Not enough memory\n");
770 memcpy(user
, output_token
.value
, output_token
.length
);
771 user
[output_token
.length
] = '\0';
772 if (norealm
&& (p
= strchr(user
, '@')) != NULL
) {
777 ret
= krb5_init_context(&context
);
778 if (!check_k5_err(context
, "krb5_init_context", ret
)) {
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
);
791 krb5_free_context(context
);
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
);
801 (void)gss_release_any_name_mapping(&minor_status
, client_name
, &type_id
, (gss_any_t
*)&pac
);
802 krb5_free_context(context
);
806 debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM
, ag
);
809 rfc_user
= rfc1738_escape(user
);
811 fprintf(stdout
, "OK token=%s user=%s %s\n", token
, rfc_user
, ag
?ag
:"group=");
813 fprintf(stdout
, "OK token=%s user=%s\n", token
, rfc_user
);
815 debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM
, token
, rfc_user
);
817 fprintf(stderr
, "%s| %s: INFO: User %s authenticated\n", LogTime(),
821 if (check_gss_err(major_status
, minor_status
, "gss_accept_sec_context()", log
, 1))
823 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
824 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM
);
825 // XXX: where to get the server token for delivery to client? token is nullptr here.
826 fprintf(stdout
, "ERR\n");
829 gss_release_buffer(&minor_status
, &output_token
);
831 gss_display_name(&minor_status
, client_name
, &output_token
,
834 if (check_gss_err(major_status
, minor_status
, "gss_display_name()", log
, 1))
837 * Return dummy token AA. May need an extra return tag then AF
839 user
= (char *) xmalloc(output_token
.length
+ 1);
841 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
842 fprintf(stdout
, "BH Not enough memory\n");
845 memcpy(user
, output_token
.value
, output_token
.length
);
846 user
[output_token
.length
] = '\0';
847 if (norealm
&& (p
= strchr(user
, '@')) != NULL
) {
850 rfc_user
= rfc1738_escape(user
);
852 fprintf(stdout
, "OK token=%s user=%s %s\n", "AA==", rfc_user
, ag
?ag
:"group=");
854 fprintf(stdout
, "OK token=%s user=%s\n", "AA==", rfc_user
);
856 debug((char *) "%s| %s: DEBUG: OK token=%s user=%s\n", LogTime(), PROGRAM
, "AA==", rfc_user
);
858 fprintf(stderr
, "%s| %s: INFO: User %s authenticated\n", LogTime(),
862 gss_release_buffer(&minor_status
, &input_token
);
863 gss_release_buffer(&minor_status
, &output_token
);
864 gss_release_cred(&minor_status
, &server_creds
);
866 gss_release_name(&minor_status
, &server_name
);
868 gss_release_name(&minor_status
, &client_name
);
870 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
872 safe_free(kerberosToken
);
875 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
876 safe_free(spnegoToken
);
886 #ifndef MAX_AUTHTOKEN_LEN
887 #define MAX_AUTHTOKEN_LEN 65535
890 main(int argc
, char *const argv
[])
892 setbuf(stdout
, NULL
);
894 char buf
[MAX_AUTHTOKEN_LEN
];
896 if (fgets(buf
, sizeof(buf
) - 1, stdin
) == NULL
) {
897 fprintf(stdout
, "BH input error\n");
900 fprintf(stdout
, "BH Kerberos authentication not supported\n");
904 #endif /* HAVE_GSSAPI */