2 * -----------------------------------------------------------------------------
4 * Author: Markus Moeller (markus_moeller at compuserve.com)
6 * Copyright (C) 2007 Markus Moeller. All rights reserved.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22 * As a special exemption, M Moeller gives permission to link this program
23 * with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
24 * the resulting executable, without including the source code for
25 * the Libraries in the source distribution.
27 * -----------------------------------------------------------------------------
30 * Hosted at http://sourceforge.net/projects/squidkerbauth
32 #include "ska_config.h"
57 #if !defined(HAVE_DECL_XGETADDRINFO) || !HAVE_DECL_XGETADDRINFO
58 #define xgetaddrinfo getaddrinfo
60 #if !defined(HAVE_DECL_XFREEADDRINFO) || !HAVE_DECL_XFREEADDRINFO
61 #define xfreeaddrinfo freeaddrinfo
63 #if !defined(HAVE_DECL_XGAI_STRERROR) || !HAVE_DECL_XGAI_STRERROR
64 #define xgai_strerror gai_strerror
66 #if !defined(HAVE_DECL_XGETNAMEINFO) || !HAVE_DECL_XGETNAMEINFO
67 #define xgetnameinfo getnameinfo
69 #if !defined(HAVE_DECL_XMALLOC) || !HAVE_DECL_XMALLOC
70 #define xmalloc malloc
72 #if !defined(HAVE_DECL_XSTRDUP) || !HAVE_DECL_XSTRDUP
73 #define xstrdup strdup
75 #if !defined(HAVE_DECL_XFREE) || !HAVE_DECL_XFREE
81 #include "spnegohelp/spnegohelp.h"
84 #define PROGRAM "squid_kerb_auth"
87 #ifndef MAX_AUTHTOKEN_LEN
88 #define MAX_AUTHTOKEN_LEN 65535
91 int check_gss_err(OM_uint32 major_status
, OM_uint32 minor_status
, const char* function
, int debug
, int log
);
92 char *gethost_name(void);
93 static const char *LogTime(void);
95 static const unsigned char ntlmProtocol
[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
97 static const char *LogTime()
101 static time_t last_t
= 0;
102 static char buf
[128];
104 gettimeofday(&now
, NULL
);
105 if (now
.tv_sec
!= last_t
) {
106 tm
= localtime((time_t *)&now
.tv_sec
);
107 strftime(buf
, 127, "%Y/%m/%d %H:%M:%S", tm
);
113 char *gethost_name(void)
115 char hostname
[sysconf(_SC_HOST_NAME_MAX
)];
116 struct addrinfo
*hres
=NULL
, *hres_list
;
119 rc
= gethostname(hostname
,sysconf(_SC_HOST_NAME_MAX
));
121 fprintf(stderr
, "%s| %s: error while resolving hostname '%s'\n", LogTime(), PROGRAM
, hostname
);
124 rc
= xgetaddrinfo(hostname
,NULL
,NULL
,&hres
);
126 fprintf(stderr
, "%s| %s: error while resolving hostname with getaddrinfo: %s\n", LogTime(), PROGRAM
, xgai_strerror(rc
));
133 hres_list
=hres_list
->ai_next
;
135 rc
= xgetnameinfo (hres
->ai_addr
, hres
->ai_addrlen
,hostname
, sizeof (hostname
), NULL
, 0, 0);
137 fprintf(stderr
, "%s| %s: error while resolving ip address with getnameinfo: %s\n", LogTime(), PROGRAM
, xgai_strerror(rc
));
143 hostname
[sysconf(_SC_HOST_NAME_MAX
)-1]='\0';
144 return(xstrdup(hostname
));
147 int check_gss_err(OM_uint32 major_status
, OM_uint32 minor_status
, const char* function
, int debug
, int log
)
149 if (GSS_ERROR(major_status
)) {
150 OM_uint32 maj_stat
,min_stat
;
151 OM_uint32 msg_ctx
= 0;
152 gss_buffer_desc status_string
;
159 /* convert major status code (GSS-API error) to text */
160 maj_stat
= gss_display_status(&min_stat
, major_status
,
163 &msg_ctx
, &status_string
);
164 if (maj_stat
== GSS_S_COMPLETE
) {
165 if (sizeof(buf
) > len
+ status_string
.length
+ 1) {
166 sprintf(buf
+len
, "%s", (char*) status_string
.value
);
167 len
+= status_string
.length
;
169 gss_release_buffer(&min_stat
, &status_string
);
172 gss_release_buffer(&min_stat
, &status_string
);
174 if (sizeof(buf
) > len
+ 2) {
175 sprintf(buf
+len
, "%s", ". ");
180 /* convert minor status code (underlying routine error) to text */
181 maj_stat
= gss_display_status(&min_stat
, minor_status
,
184 &msg_ctx
, &status_string
);
185 if (maj_stat
== GSS_S_COMPLETE
) {
186 if (sizeof(buf
) > len
+ status_string
.length
) {
187 sprintf(buf
+len
, "%s", (char*) status_string
.value
);
188 len
+= status_string
.length
;
190 gss_release_buffer(&min_stat
, &status_string
);
193 gss_release_buffer(&min_stat
, &status_string
);
196 fprintf(stderr
, "%s| %s: %s failed: %s\n", LogTime(), PROGRAM
, function
, buf
);
197 fprintf(stdout
, "BH %s failed: %s\n",function
, buf
);
199 fprintf(stderr
, "%s| %s: User not authenticated\n", LogTime(), PROGRAM
);
207 int main(int argc
, char * const argv
[])
209 char buf
[MAX_AUTHTOKEN_LEN
];
214 int opt
, debug
=0, log
=0;
218 OM_uint32 ret_flags
=0, spnego_flag
=0;
219 char *service_name
=(char *)"HTTP",*host_name
=NULL
;
221 char *service_principal
= NULL
;
222 OM_uint32 major_status
, minor_status
;
223 gss_ctx_id_t gss_context
= GSS_C_NO_CONTEXT
;
224 gss_name_t client_name
= GSS_C_NO_NAME
;
225 gss_name_t server_name
= GSS_C_NO_NAME
;
226 gss_cred_id_t server_creds
= GSS_C_NO_CREDENTIAL
;
227 gss_buffer_desc service
= GSS_C_EMPTY_BUFFER
;
228 gss_buffer_desc input_token
= GSS_C_EMPTY_BUFFER
;
229 gss_buffer_desc output_token
= GSS_C_EMPTY_BUFFER
;
230 const unsigned char *kerberosToken
= NULL
;
232 size_t kerberosTokenLength
= 0;
234 const unsigned char *spnegoToken
= NULL
;
235 size_t spnegoTokenLength
= 0;
240 while (-1 != (opt
= getopt(argc
, argv
, "dis:h"))) {
249 service_principal
= xstrdup(optarg
);
252 fprintf(stderr
, "Usage: \n");
253 fprintf(stderr
, "squid_kerb_auth [-d] [-i] [-s SPN] [-h]\n");
254 fprintf(stderr
, "-d full debug\n");
255 fprintf(stderr
, "-i informational messages\n");
256 fprintf(stderr
, "-s service principal name\n");
257 fprintf(stderr
, "-h help\n");
258 fprintf(stderr
, "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
259 fprintf(stderr
, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
262 fprintf(stderr
, "%s| %s: unknown option: -%c.\n", LogTime(), PROGRAM
, opt
);
267 fprintf(stderr
, "%s| %s: Starting version %s\n", LogTime(), PROGRAM
, VERSION
);
268 if (service_principal
&& strcasecmp(service_principal
,"GSS_C_NO_NAME") ) {
269 service
.value
= service_principal
;
270 service
.length
= strlen((char *)service
.value
);
272 host_name
=gethost_name();
274 fprintf(stderr
, "%s| %s: Local hostname could not be determined. Please specify the service principal\n", LogTime(), PROGRAM
);
275 fprintf(stdout
, "BH hostname error\n");
278 service
.value
= xmalloc(strlen(service_name
)+strlen(host_name
)+2);
279 snprintf(service
.value
,strlen(service_name
)+strlen(host_name
)+2,"%s@%s",service_name
,host_name
);
280 service
.length
= strlen((char *)service
.value
);
284 if (fgets(buf
, sizeof(buf
)-1, stdin
) == NULL
) {
287 fprintf(stderr
, "%s| %s: fgets() failed! dying..... errno=%d (%s)\n", LogTime(), PROGRAM
, ferror(stdin
),
288 strerror(ferror(stdin
)));
290 fprintf(stdout
, "BH input error\n");
291 exit(1); /* BIIG buffer */
293 fprintf(stdout
, "BH input error\n");
297 c
=memchr(buf
,'\n',sizeof(buf
)-1);
306 fprintf(stderr
, "%s| %s: Oversized message\n", LogTime(), PROGRAM
);
307 fprintf(stdout
, "BH Oversized message\n");
313 fprintf(stderr
, "%s| %s: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM
, buf
,length
);
315 if (buf
[0] == '\0') {
317 fprintf(stderr
, "%s| %s: Invalid request\n", LogTime(), PROGRAM
);
318 fprintf(stdout
, "BH Invalid request\n");
322 if (strlen(buf
) < 2) {
324 fprintf(stderr
, "%s| %s: Invalid request [%s]\n", LogTime(), PROGRAM
, buf
);
325 fprintf(stdout
, "BH Invalid request\n");
329 if ( !strncmp(buf
, "QQ", 2) ) {
330 gss_release_buffer(&minor_status
, &input_token
);
331 gss_release_buffer(&minor_status
, &output_token
);
332 gss_release_buffer(&minor_status
, &service
);
333 gss_release_cred(&minor_status
, &server_creds
);
335 gss_release_name(&minor_status
, &server_name
);
337 gss_release_name(&minor_status
, &client_name
);
338 if (gss_context
!= GSS_C_NO_CONTEXT
)
339 gss_delete_sec_context(&minor_status
, &gss_context
, NULL
);
341 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
343 xfree((char *)kerberosToken
);
347 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
349 xfree((char *)spnegoToken
);
360 fprintf(stdout
, "BH quit command\n");
364 if ( strncmp(buf
, "YR", 2) && strncmp(buf
, "KK", 2) ) {
366 fprintf(stderr
, "%s| %s: Invalid request [%s]\n", LogTime(), PROGRAM
, buf
);
367 fprintf(stdout
, "BH Invalid request\n");
370 if ( !strncmp(buf
, "YR", 2) ) {
371 if (gss_context
!= GSS_C_NO_CONTEXT
)
372 gss_delete_sec_context(&minor_status
, &gss_context
, NULL
);
373 gss_context
= GSS_C_NO_CONTEXT
;
376 if (strlen(buf
) <= 3) {
378 fprintf(stderr
, "%s| %s: Invalid negotiate request [%s]\n", LogTime(), PROGRAM
, buf
);
379 fprintf(stdout
, "BH Invalid negotiate request\n");
383 input_token
.length
= ska_base64_decode_len(buf
+3);
385 fprintf(stderr
, "%s| %s: Decode '%s' (decoded length: %d).\n", LogTime(), PROGRAM
, buf
+3,(int)input_token
.length
);
386 input_token
.value
= xmalloc(input_token
.length
);
388 ska_base64_decode(input_token
.value
,buf
+3,input_token
.length
);
392 if (( rc
=parseNegTokenInit (input_token
.value
,
395 &kerberosTokenLength
))!=0 ) {
397 fprintf(stderr
, "%s| %s: parseNegTokenInit failed with rc=%d\n", LogTime(), PROGRAM
, rc
);
399 /* if between 100 and 200 it might be a GSSAPI token and not a SPNEGO token */
400 if ( rc
< 100 || rc
> 199 ) {
402 fprintf(stderr
, "%s| %s: Invalid GSS-SPNEGO query [%s]\n", LogTime(), PROGRAM
, buf
);
403 fprintf(stdout
, "BH Invalid GSS-SPNEGO query\n");
406 if ((input_token
.length
>= sizeof ntlmProtocol
+ 1) &&
407 (!memcmp (input_token
.value
, ntlmProtocol
, sizeof ntlmProtocol
))) {
409 fprintf(stderr
, "%s| %s: received type %d NTLM token\n", LogTime(), PROGRAM
, (int) *((unsigned char *)input_token
.value
+ sizeof ntlmProtocol
));
410 fprintf(stdout
, "BH received type %d NTLM token\n",(int) *((unsigned char *)input_token
.value
+ sizeof ntlmProtocol
));
414 fprintf(stderr
, "%s| %s: Token is possibly a GSSAPI token\n", LogTime(), PROGRAM
);
417 gss_release_buffer(&minor_status
, &input_token
);
418 input_token
.length
=kerberosTokenLength
;
419 input_token
.value
=(void *)kerberosToken
;
423 if ((input_token
.length
>= sizeof ntlmProtocol
+ 1) &&
424 (!memcmp (input_token
.value
, ntlmProtocol
, sizeof ntlmProtocol
))) {
426 fprintf(stderr
, "%s| %s: received type %d NTLM token\n", LogTime(), PROGRAM
, (int) *((unsigned char *)input_token
.value
+ sizeof ntlmProtocol
));
427 fprintf(stdout
, "BH received type %d NTLM token\n",(int) *((unsigned char *)input_token
.value
+ sizeof ntlmProtocol
));
432 if ( service_principal
) {
433 if ( strcasecmp(service_principal
,"GSS_C_NO_NAME") ) {
434 major_status
= gss_import_name(&minor_status
, &service
,
435 (gss_OID
) GSS_C_NULL_OID
, &server_name
);
438 server_name
= GSS_C_NO_NAME
;
439 major_status
= GSS_S_COMPLETE
;
442 major_status
= gss_import_name(&minor_status
, &service
,
443 gss_nt_service_name
, &server_name
);
446 if ( check_gss_err(major_status
,minor_status
,"gss_import_name()",debug
,log
) )
449 major_status
= gss_acquire_cred(&minor_status
, server_name
, GSS_C_INDEFINITE
,
450 GSS_C_NO_OID_SET
, GSS_C_ACCEPT
, &server_creds
,
452 if (check_gss_err(major_status
,minor_status
,"gss_acquire_cred()",debug
,log
) )
455 major_status
= gss_accept_sec_context(&minor_status
,
459 GSS_C_NO_CHANNEL_BINDINGS
,
468 if (output_token
.length
) {
471 if ((rc
=makeNegTokenTarg (output_token
.value
,
474 &spnegoTokenLength
))!=0 ) {
476 fprintf(stderr
, "%s| %s: makeNegTokenTarg failed with rc=%d\n", LogTime(), PROGRAM
, rc
);
477 fprintf(stdout
, "BH makeNegTokenTarg failed with rc=%d\n",rc
);
481 spnegoToken
= output_token
.value
;
482 spnegoTokenLength
= output_token
.length
;
485 spnegoToken
= output_token
.value
;
486 spnegoTokenLength
= output_token
.length
;
488 token
= xmalloc(ska_base64_encode_len(spnegoTokenLength
));
491 fprintf(stderr
, "%s| %s: Not enough memory\n", LogTime(), PROGRAM
);
492 fprintf(stdout
, "BH Not enough memory\n");
496 ska_base64_encode(token
,(const char *)spnegoToken
,ska_base64_encode_len(spnegoTokenLength
),spnegoTokenLength
);
498 if (check_gss_err(major_status
,minor_status
,"gss_accept_sec_context()",debug
,log
) )
500 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
502 fprintf(stderr
, "%s| %s: continuation needed\n", LogTime(), PROGRAM
);
503 fprintf(stdout
, "TT %s\n",token
);
506 gss_release_buffer(&minor_status
, &output_token
);
507 major_status
= gss_display_name(&minor_status
, client_name
, &output_token
,
510 if (check_gss_err(major_status
,minor_status
,"gss_display_name()",debug
,log
) )
512 user
=xmalloc(output_token
.length
+1);
515 fprintf(stderr
, "%s| %s: Not enough memory\n", LogTime(), PROGRAM
);
516 fprintf(stdout
, "BH Not enough memory\n");
519 memcpy(user
,output_token
.value
,output_token
.length
);
520 user
[output_token
.length
]='\0';
521 fprintf(stdout
, "AF %s %s\n",token
,user
);
523 fprintf(stderr
, "%s| %s: AF %s %s\n", LogTime(), PROGRAM
, token
,user
);
525 fprintf(stderr
, "%s| %s: User %s authenticated\n", LogTime(), PROGRAM
, user
);
528 if (check_gss_err(major_status
,minor_status
,"gss_accept_sec_context()",debug
,log
) )
530 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
532 fprintf(stderr
, "%s| %s: continuation needed\n", LogTime(), PROGRAM
);
533 fprintf(stdout
, "NA %s\n",token
);
536 gss_release_buffer(&minor_status
, &output_token
);
537 major_status
= gss_display_name(&minor_status
, client_name
, &output_token
,
540 if (check_gss_err(major_status
,minor_status
,"gss_display_name()",debug
,log
) )
543 * Return dummy token AA. May need an extra return tag then AF
545 user
=xmalloc(output_token
.length
+1);
548 fprintf(stderr
, "%s| %s: Not enough memory\n", LogTime(), PROGRAM
);
549 fprintf(stdout
, "BH Not enough memory\n");
552 memcpy(user
,output_token
.value
,output_token
.length
);
553 user
[output_token
.length
]='\0';
554 fprintf(stdout
, "AF %s %s\n","AA==",user
);
556 fprintf(stderr
, "%s| %s: AF %s %s\n", LogTime(), PROGRAM
, "AA==", user
);
558 fprintf(stderr
, "%s| %s: User %s authenticated\n", LogTime(), PROGRAM
, user
);
561 gss_release_buffer(&minor_status
, &input_token
);
562 gss_release_buffer(&minor_status
, &output_token
);
563 gss_release_cred(&minor_status
, &server_creds
);
565 gss_release_name(&minor_status
, &server_name
);
567 gss_release_name(&minor_status
, &client_name
);
569 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
571 xfree((char *)kerberosToken
);
575 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
577 xfree((char *)spnegoToken
);