2 * Copyright (C) 1996-2017 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 */
69 check_k5_err(krb5_context context
, const char *function
, krb5_error_code code
)
72 if (code
&& code
!= KRB5_KT_END
) {
74 errmsg
= krb5_get_error_message(context
, code
);
75 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM
, function
, errmsg
);
76 fprintf(stderr
, "%s| %s: ERROR: %s: %s\n", LogTime(), PROGRAM
, function
, errmsg
);
77 #if HAVE_KRB5_FREE_ERROR_MESSAGE
78 krb5_free_error_message(context
, errmsg
);
79 #elif HAVE_KRB5_FREE_ERROR_STRING
80 krb5_free_error_string(context
, (char *)errmsg
);
92 * char hostname[sysconf(_SC_HOST_NAME_MAX)];
95 struct addrinfo
*hres
= NULL
, *hres_list
;
98 rc
= gethostname(hostname
, sizeof(hostname
)-1);
100 debug((char *) "%s| %s: ERROR: resolving hostname '%s' failed\n", LogTime(), PROGRAM
, hostname
);
101 fprintf(stderr
, "%s| %s: ERROR: resolving hostname '%s' failed\n",
102 LogTime(), PROGRAM
, hostname
);
105 rc
= getaddrinfo(hostname
, NULL
, NULL
, &hres
);
106 if (rc
!= 0 || hres
== NULL
) {
107 debug((char *) "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
108 LogTime(), PROGRAM
, gai_strerror(rc
));
110 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
111 LogTime(), PROGRAM
, gai_strerror(rc
));
118 hres_list
= hres_list
->ai_next
;
120 rc
= getnameinfo(hres
->ai_addr
, hres
->ai_addrlen
, hostname
,
121 sizeof(hostname
), NULL
, 0, 0);
123 debug((char *) "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
124 LogTime(), PROGRAM
, gai_strerror(rc
));
126 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
127 LogTime(), PROGRAM
, gai_strerror(rc
));
132 hostname
[sizeof(hostname
)-1] = '\0';
133 return (xstrdup(hostname
));
137 check_gss_err(OM_uint32 major_status
, OM_uint32 minor_status
,
138 const char *function
, int log
, int sout
)
140 if (GSS_ERROR(major_status
)) {
141 OM_uint32 maj_stat
, min_stat
;
142 OM_uint32 msg_ctx
= 0;
143 gss_buffer_desc status_string
;
150 /* convert major status code (GSS-API error) to text */
151 maj_stat
= gss_display_status(&min_stat
, major_status
,
152 GSS_C_GSS_CODE
, GSS_C_NULL_OID
, &msg_ctx
, &status_string
);
153 if (maj_stat
== GSS_S_COMPLETE
&& status_string
.length
> 0) {
154 if (sizeof(buf
) > len
+ status_string
.length
+ 1) {
155 snprintf(buf
+ len
, (sizeof(buf
) - len
), "%s", (char *) status_string
.value
);
156 len
+= status_string
.length
;
160 gss_release_buffer(&min_stat
, &status_string
);
162 if (sizeof(buf
) > len
+ 2) {
163 snprintf(buf
+ len
, (sizeof(buf
) - len
), "%s", ". ");
168 /* convert minor status code (underlying routine error) to text */
169 maj_stat
= gss_display_status(&min_stat
, minor_status
,
170 GSS_C_MECH_CODE
, GSS_C_NULL_OID
, &msg_ctx
, &status_string
);
171 if (maj_stat
== GSS_S_COMPLETE
&& status_string
.length
> 0) {
172 if (sizeof(buf
) > len
+ status_string
.length
) {
173 snprintf(buf
+ len
, (sizeof(buf
) - len
), "%s", (char *) status_string
.value
);
174 len
+= status_string
.length
;
178 gss_release_buffer(&min_stat
, &status_string
);
180 debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM
, function
, buf
);
182 fprintf(stdout
, "BH %s failed: %s\n", function
, buf
);
184 fprintf(stderr
, "%s| %s: INFO: User not authenticated\n", LogTime(),
191 #if HAVE_KRB5_MEMORY_KEYTAB
195 krb5_error_code
krb5_free_kt_list(krb5_context context
, krb5_kt_list list
)
197 krb5_kt_list lp
= list
;
200 #if USE_HEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY )
201 krb5_error_code retval
= krb5_kt_free_entry(context
, lp
->entry
);
203 krb5_error_code retval
= krb5_free_keytab_entry_contents(context
, lp
->entry
);
205 safe_free(lp
->entry
);
206 if (check_k5_err(context
, "krb5_kt_free_entry", retval
))
208 krb5_kt_list prev
= lp
;
215 * Read in a keytab and append it to list. If list starts as NULL,
216 * allocate a new one if necessary.
218 krb5_error_code
krb5_read_keytab(krb5_context context
, char *name
, krb5_kt_list
*list
)
220 krb5_kt_list lp
= NULL
, tail
= NULL
, back
= NULL
;
222 krb5_keytab_entry
*entry
;
223 krb5_kt_cursor cursor
;
224 krb5_error_code retval
= 0;
227 /* point lp at the tail of the list */
228 for (lp
= *list
; lp
->next
; lp
= lp
->next
);
231 retval
= krb5_kt_resolve(context
, name
, &kt
);
232 if (check_k5_err(context
, "krb5_kt_resolve", retval
))
234 retval
= krb5_kt_start_seq_get(context
, kt
, &cursor
);
235 if (check_k5_err(context
, "krb5_kt_start_seq_get", retval
))
238 entry
= (krb5_keytab_entry
*)xcalloc(1, sizeof (krb5_keytab_entry
));
241 debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
242 LogTime(), PROGRAM
, strerror(retval
));
243 fprintf(stderr
, "%s| %s: ERROR: krb5_read_keytab: %s\n",
244 LogTime(), PROGRAM
, strerror(retval
));
247 memset(entry
, 0, sizeof (*entry
));
248 retval
= krb5_kt_next_entry(context
, kt
, entry
, &cursor
);
249 if (check_k5_err(context
, "krb5_kt_next_entry", retval
))
252 if (!lp
) { /* if list is empty, start one */
253 lp
= (krb5_kt_list
)xmalloc(sizeof (*lp
));
256 debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
257 LogTime(), PROGRAM
, strerror(retval
));
258 fprintf(stderr
, "%s| %s: ERROR: krb5_read_keytab: %s\n",
259 LogTime(), PROGRAM
, strerror(retval
));
263 lp
->next
= (krb5_kt_list
)xmalloc(sizeof (*lp
));
266 debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
267 LogTime(), PROGRAM
, strerror(retval
));
268 fprintf(stderr
, "%s| %s: ERROR: krb5_read_keytab: %s\n",
269 LogTime(), PROGRAM
, strerror(retval
));
281 if (retval
== KRB5_KT_END
)
284 krb5_free_kt_list(context
, tail
);
292 krb5_kt_end_seq_get(context
, kt
, &cursor
);
294 krb5_kt_close(context
, kt
);
299 * Takes a kt_list and writes it to the named keytab.
301 krb5_error_code
krb5_write_keytab(krb5_context context
, krb5_kt_list list
, char *name
)
304 char ktname
[MAXPATHLEN
+sizeof("MEMORY:")+1];
305 krb5_error_code retval
= 0;
307 snprintf(ktname
, sizeof(ktname
), "%s", name
);
308 retval
= krb5_kt_resolve(context
, ktname
, &kt
);
311 for (krb5_kt_list lp
= list
; lp
; lp
= lp
->next
) {
312 retval
= krb5_kt_add_entry(context
, kt
, lp
->entry
);
317 * krb5_kt_close(context, kt);
321 #endif /* HAVE_KRB5_MEMORY_KEYTAB */
324 main(int argc
, char *const argv
[])
326 char buf
[MAX_AUTHTOKEN_LEN
];
329 char *rfc_user
= NULL
;
331 char ad_groups
[MAX_PAC_GROUP_SIZE
];
335 gss_buffer_desc data_set
= GSS_C_EMPTY_BUFFER
;
337 gss_buffer_desc type_id
= GSS_C_EMPTY_BUFFER
;
340 krb5_context context
= NULL
;
344 int opt
, log
= 0, norealm
= 0;
345 OM_uint32 ret_flags
= 0, spnego_flag
= 0;
346 char *service_name
= (char *) "HTTP", *host_name
= NULL
;
348 char *service_principal
= NULL
;
349 char *keytab_name
= NULL
;
350 char *keytab_name_env
= NULL
;
351 char default_keytab
[MAXPATHLEN
];
352 #if HAVE_KRB5_MEMORY_KEYTAB
353 char *memory_keytab_name
= NULL
;
355 char *rcache_type
= NULL
;
356 char *rcache_type_env
= NULL
;
357 char *rcache_dir
= NULL
;
358 char *rcache_dir_env
= NULL
;
359 OM_uint32 major_status
, minor_status
;
360 gss_ctx_id_t gss_context
= GSS_C_NO_CONTEXT
;
361 gss_name_t client_name
= GSS_C_NO_NAME
;
362 gss_name_t server_name
= GSS_C_NO_NAME
;
363 gss_cred_id_t server_creds
= GSS_C_NO_CREDENTIAL
;
364 gss_buffer_desc service
= GSS_C_EMPTY_BUFFER
;
365 gss_buffer_desc input_token
= GSS_C_EMPTY_BUFFER
;
366 gss_buffer_desc output_token
= GSS_C_EMPTY_BUFFER
;
367 const unsigned char *kerberosToken
= NULL
;
368 const unsigned char *spnegoToken
= NULL
;
369 size_t spnegoTokenLength
= 0;
371 setbuf(stdout
, NULL
);
374 while (-1 != (opt
= getopt(argc
, argv
, "dirs:k:c:t:"))) {
391 keytab_name
= xstrdup(optarg
);
393 fprintf(stderr
, "ERROR: keytab file not given\n");
400 if ((ktp
=strchr(keytab_name
,':')))
404 if (stat((const char*)ktp
, &fstat
)) {
406 fprintf(stderr
, "ERROR: keytab file %s does not exist\n",keytab_name
);
408 fprintf(stderr
, "ERROR: Error %s during stat of keytab file %s\n",strerror(errno
),keytab_name
);
410 } else if (!S_ISREG(fstat
.st_mode
)) {
411 fprintf(stderr
, "ERROR: keytab file %s is not a file\n",keytab_name
);
416 if (access(ktp
, R_OK
)) {
417 fprintf(stderr
, "ERROR: keytab file %s is not accessible\n",keytab_name
);
427 rcache_dir
= xstrdup(optarg
);
429 fprintf(stderr
, "ERROR: replay cache directory not given\n");
436 if (stat((const char*)rcache_dir
, &dstat
)) {
438 fprintf(stderr
, "ERROR: replay cache directory %s does not exist\n",rcache_dir
);
440 fprintf(stderr
, "ERROR: Error %s during stat of replay cache directory %s\n",strerror(errno
),rcache_dir
);
442 } else if (!S_ISDIR(dstat
.st_mode
)) {
443 fprintf(stderr
, "ERROR: replay cache directory %s is not a directory\n",rcache_dir
);
448 if (access(rcache_dir
, W_OK
)) {
449 fprintf(stderr
, "ERROR: replay cache directory %s is not accessible\n",rcache_dir
);
456 rcache_type
= xstrdup(optarg
);
458 fprintf(stderr
, "ERROR: replay cache type not given\n");
464 service_principal
= xstrdup(optarg
);
466 fprintf(stderr
, "ERROR: service principal not given\n");
471 fprintf(stderr
, "Usage: \n");
472 fprintf(stderr
, "squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n");
473 fprintf(stderr
, "-d full debug\n");
474 fprintf(stderr
, "-i informational messages\n");
475 fprintf(stderr
, "-r remove realm from username\n");
476 fprintf(stderr
, "-s service principal name\n");
477 fprintf(stderr
, "-k keytab name\n");
478 fprintf(stderr
, "-c replay cache directory\n");
479 fprintf(stderr
, "-t replay cache type\n");
481 "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
482 fprintf(stderr
, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
487 debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM
, SQUID_KERB_AUTH_VERSION
);
488 if (service_principal
&& strcasecmp(service_principal
, "GSS_C_NO_NAME")) {
489 if (!strstr(service_principal
,"HTTP/")) {
490 debug((char *) "%s| %s: WARN: service_principal %s does not start with HTTP/\n",
491 LogTime(), PROGRAM
, service_principal
);
493 service
.value
= service_principal
;
494 service
.length
= strlen((char *) service
.value
);
496 host_name
= gethost_name();
499 "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
501 fprintf(stdout
, "BH hostname error\n");
504 service
.value
= xmalloc(strlen(service_name
) + strlen(host_name
) + 2);
505 snprintf((char *) service
.value
, strlen(service_name
) + strlen(host_name
) + 2,
506 "%s@%s", service_name
, host_name
);
507 service
.length
= strlen((char *) service
.value
);
512 rcache_type_env
= (char *) xmalloc(strlen("KRB5RCACHETYPE=")+strlen(rcache_type
)+1);
513 strcpy(rcache_type_env
, "KRB5RCACHETYPE=");
514 strcat(rcache_type_env
, rcache_type
);
515 putenv(rcache_type_env
);
516 debug((char *) "%s| %s: INFO: Setting replay cache type to %s\n",
517 LogTime(), PROGRAM
, rcache_type
);
521 rcache_dir_env
= (char *) xmalloc(strlen("KRB5RCACHEDIR=")+strlen(rcache_dir
)+1);
522 strcpy(rcache_dir_env
, "KRB5RCACHEDIR=");
523 strcat(rcache_dir_env
, rcache_dir
);
524 putenv(rcache_dir_env
);
525 debug((char *) "%s| %s: INFO: Setting replay cache directory to %s\n",
526 LogTime(), PROGRAM
, rcache_dir
);
530 keytab_name_env
= (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(keytab_name
)+1);
531 strcpy(keytab_name_env
, "KRB5_KTNAME=");
532 strcat(keytab_name_env
, keytab_name
);
533 putenv(keytab_name_env
);
535 keytab_name_env
= getenv("KRB5_KTNAME");
536 if (!keytab_name_env
) {
537 ret
= krb5_init_context(&context
);
538 if (!check_k5_err(context
, "krb5_init_context", ret
)) {
539 krb5_kt_default_name(context
, default_keytab
, MAXPATHLEN
);
541 keytab_name
= xstrdup(default_keytab
);
542 krb5_free_context(context
);
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 const uint8_t *b64Token
= reinterpret_cast<const uint8_t*>(buf
+3);
662 const size_t srcLen
= strlen(buf
+3);
663 input_token
.length
= BASE64_DECODE_LENGTH(srcLen
);
664 debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n",
665 LogTime(), PROGRAM
, b64Token
, (int) input_token
.length
);
666 input_token
.value
= xmalloc(input_token
.length
);
668 struct base64_decode_ctx ctx
;
669 base64_decode_init(&ctx
);
671 if (!base64_decode_update(&ctx
, &dstLen
, static_cast<uint8_t*>(input_token
.value
), srcLen
, b64Token
) ||
672 !base64_decode_final(&ctx
)) {
673 debug((char *) "%s| %s: ERROR: Invalid base64 token [%s]\n", LogTime(), PROGRAM
, b64Token
);
674 fprintf(stdout
, "BH Invalid negotiate request token\n");
677 input_token
.length
= dstLen
;
679 if ((input_token
.length
>= sizeof ntlmProtocol
+ 1) &&
680 (!memcmp(input_token
.value
, ntlmProtocol
, sizeof ntlmProtocol
))) {
681 debug((char *) "%s| %s: WARNING: received type %d NTLM token\n",
683 (int) *((unsigned char *) input_token
.value
+
684 sizeof ntlmProtocol
));
685 fprintf(stdout
, "BH received type %d NTLM token\n",
686 (int) *((unsigned char *) input_token
.value
+
687 sizeof ntlmProtocol
));
690 if (service_principal
) {
691 if (strcasecmp(service_principal
, "GSS_C_NO_NAME")) {
692 major_status
= gss_import_name(&minor_status
, &service
,
693 (gss_OID
) GSS_C_NULL_OID
, &server_name
);
696 server_name
= GSS_C_NO_NAME
;
697 major_status
= GSS_S_COMPLETE
;
701 major_status
= gss_import_name(&minor_status
, &service
,
702 gss_nt_service_name
, &server_name
);
705 if (check_gss_err(major_status
, minor_status
, "gss_import_name()", log
, 1))
709 gss_acquire_cred(&minor_status
, server_name
, GSS_C_INDEFINITE
,
710 GSS_C_NO_OID_SET
, GSS_C_ACCEPT
, &server_creds
, NULL
, NULL
);
711 if (check_gss_err(major_status
, minor_status
, "gss_acquire_cred()", log
, 1))
714 major_status
= gss_accept_sec_context(&minor_status
,
718 GSS_C_NO_CHANNEL_BINDINGS
,
719 &client_name
, NULL
, &output_token
, &ret_flags
, NULL
, NULL
);
721 if (output_token
.length
) {
722 spnegoToken
= (const unsigned char *) output_token
.value
;
723 spnegoTokenLength
= output_token
.length
;
724 token
= (char *) xmalloc((size_t)base64_encode_len(spnegoTokenLength
));
726 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
727 fprintf(stdout
, "BH Not enough memory\n");
730 struct base64_encode_ctx tokCtx
;
731 base64_encode_init(&tokCtx
);
732 size_t blen
= base64_encode_update(&tokCtx
, reinterpret_cast<uint8_t*>(token
), spnegoTokenLength
, reinterpret_cast<const uint8_t*>(spnegoToken
));
733 blen
+= base64_encode_final(&tokCtx
, reinterpret_cast<uint8_t*>(token
)+blen
);
736 if (check_gss_err(major_status
, minor_status
, "gss_accept_sec_context()", log
, 1))
738 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
739 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM
);
740 fprintf(stdout
, "TT %s\n", token
);
743 gss_release_buffer(&minor_status
, &output_token
);
745 gss_display_name(&minor_status
, client_name
, &output_token
,
748 if (check_gss_err(major_status
, minor_status
, "gss_display_name()", log
, 1))
750 user
= (char *) xmalloc(output_token
.length
+ 1);
752 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
753 fprintf(stdout
, "BH Not enough memory\n");
756 memcpy(user
, output_token
.value
, output_token
.length
);
757 user
[output_token
.length
] = '\0';
758 if (norealm
&& (p
= strchr(user
, '@')) != NULL
) {
763 ret
= krb5_init_context(&context
);
764 if (!check_k5_err(context
, "krb5_init_context", ret
)) {
766 #define ADWIN2KPAC 128
767 major_status
= gsskrb5_extract_authz_data_from_sec_context(&minor_status
,
768 gss_context
, ADWIN2KPAC
, &data_set
);
769 if (!check_gss_err(major_status
, minor_status
,
770 "gsskrb5_extract_authz_data_from_sec_context()", log
, 0)) {
771 ret
= krb5_pac_parse(context
, data_set
.value
, data_set
.length
, &pac
);
772 gss_release_buffer(&minor_status
, &data_set
);
773 if (!check_k5_err(context
, "krb5_pac_parse", ret
)) {
774 ag
= get_ad_groups((char *)&ad_groups
, context
, pac
);
775 krb5_pac_free(context
, pac
);
777 krb5_free_context(context
);
780 type_id
.value
= (void *)"mspac";
781 type_id
.length
= strlen((char *)type_id
.value
);
782 #define KRB5PACLOGONINFO 1
783 major_status
= gss_map_name_to_any(&minor_status
, client_name
, KRB5PACLOGONINFO
, &type_id
, (gss_any_t
*)&pac
);
784 if (!check_gss_err(major_status
, minor_status
, "gss_map_name_to_any()", log
, 0)) {
785 ag
= get_ad_groups((char *)&ad_groups
,context
, pac
);
787 (void)gss_release_any_name_mapping(&minor_status
, client_name
, &type_id
, (gss_any_t
*)&pac
);
788 krb5_free_context(context
);
792 debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM
, ag
);
795 rfc_user
= rfc1738_escape(user
);
797 fprintf(stdout
, "AF %s %s %s\n", token
, rfc_user
, ag
?ag
:"group=");
799 fprintf(stdout
, "AF %s %s\n", token
, rfc_user
);
801 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM
, token
, rfc_user
);
803 fprintf(stderr
, "%s| %s: INFO: User %s authenticated\n", LogTime(),
807 if (check_gss_err(major_status
, minor_status
, "gss_accept_sec_context()", log
, 1))
809 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
810 debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM
);
811 fprintf(stdout
, "NA %s\n", token
);
814 gss_release_buffer(&minor_status
, &output_token
);
816 gss_display_name(&minor_status
, client_name
, &output_token
,
819 if (check_gss_err(major_status
, minor_status
, "gss_display_name()", log
, 1))
822 * Return dummy token AA. May need an extra return tag then AF
824 user
= (char *) xmalloc(output_token
.length
+ 1);
826 debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM
);
827 fprintf(stdout
, "BH Not enough memory\n");
830 memcpy(user
, output_token
.value
, output_token
.length
);
831 user
[output_token
.length
] = '\0';
832 if (norealm
&& (p
= strchr(user
, '@')) != NULL
) {
835 rfc_user
= rfc1738_escape(user
);
837 fprintf(stdout
, "AF %s %s %s\n", "AA==", rfc_user
, ag
?ag
:"group=");
839 fprintf(stdout
, "AF %s %s\n", "AA==", rfc_user
);
841 debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM
, "AA==", rfc_user
);
843 fprintf(stderr
, "%s| %s: INFO: User %s authenticated\n", LogTime(),
847 gss_release_buffer(&minor_status
, &input_token
);
848 gss_release_buffer(&minor_status
, &output_token
);
849 gss_release_cred(&minor_status
, &server_creds
);
851 gss_release_name(&minor_status
, &server_name
);
853 gss_release_name(&minor_status
, &client_name
);
855 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
857 safe_free(kerberosToken
);
860 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
861 safe_free(spnegoToken
);
870 #ifndef MAX_AUTHTOKEN_LEN
871 #define MAX_AUTHTOKEN_LEN 65535
874 main(int argc
, char *const argv
[])
876 setbuf(stdout
, NULL
);
878 char buf
[MAX_AUTHTOKEN_LEN
];
880 if (fgets(buf
, sizeof(buf
) - 1, stdin
) == NULL
) {
881 fprintf(stdout
, "BH input error\n");
884 fprintf(stdout
, "BH Kerberos authentication not supported\n");
887 #endif /* HAVE_GSSAPI */