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 * -----------------------------------------------------------------------------
25 * Hosted at http://sourceforge.net/projects/squidkerbauth
35 #include "getaddrinfo.h"
36 #include "getnameinfo.h"
40 #include "spnegohelp.h"
43 // AYJ: must match the definition in src/auth/negotiate/auth_negotiate.cc
44 #define MAX_AUTHTOKEN_LEN 32768
46 // AYJ: match define in include/rfc2181.h
48 #define HOST_NAME_MAX 256
50 #ifndef MAXHOSTNAMELEN
51 #define MAXHOSTNAMELEN HOST_NAME_MAX
54 #define PROGRAM "squid_kerb_auth"
58 #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
60 #include <gssapi/gssapi.h>
62 #include <gssapi/gssapi_generic.h>
64 #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
69 int check_gss_err(OM_uint32 major_status
, OM_uint32 minor_status
, const char* function
, int debug
, int loging
);
70 char *gethost_name(void);
71 static const char *LogTime(void);
73 static const unsigned char ntlmProtocol
[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
75 static const char *LogTime()
79 static time_t last_t
= 0;
82 gettimeofday(&now
, NULL
);
83 if (now
.tv_sec
!= last_t
) {
84 tm
= localtime(&now
.tv_sec
);
85 strftime(buf
, 127, "%Y/%m/%d %H:%M:%S", tm
);
91 // AYJ: this looks like a duplicate of the lib/gethostname function */
92 char *gethost_name(void) {
93 char hostname
[MAXHOSTNAMELEN
];
94 struct addrinfo
*hres
=NULL
, *hres_list
;
97 rc
= gethostname(hostname
,MAXHOSTNAMELEN
);
100 fprintf(stderr
, "%s| %s: error while resolving hostname '%s'\n", LogTime(), PROGRAM
, hostname
);
103 rc
= xgetaddrinfo(hostname
,NULL
,NULL
,&hres
);
105 fprintf(stderr
, "%s| %s: error while resolving hostname with getaddrinfo: %s\n", LogTime(), PROGRAM
, xgai_strerror(rc
));
113 hres_list
=hres_list
->ai_next
;
115 rc
= xgetnameinfo(hres
->ai_addr
, hres
->ai_addrlen
,hostname
, sizeof (hostname
), NULL
, 0, 0);
117 fprintf(stderr
, "%s| %s: error while resolving ip address with getnameinfo: %s\n", LogTime(), PROGRAM
, xgai_strerror(rc
));
123 hostname
[MAXHOSTNAMELEN
]='\0';
124 return(strdup(hostname
));
127 int check_gss_err(OM_uint32 major_status
, OM_uint32 minor_status
, const char* function
, int debug
, int loging
) {
128 if (GSS_ERROR(major_status
)) {
129 OM_uint32 maj_stat
,min_stat
;
130 OM_uint32 msg_ctx
= 0;
131 gss_buffer_desc status_string
;
138 /* convert major status code (GSS-API error) to text */
139 maj_stat
= gss_display_status(&min_stat
, major_status
,
142 &msg_ctx
, &status_string
);
143 if (maj_stat
== GSS_S_COMPLETE
) {
144 if (sizeof(buf
) > len
+ status_string
.length
+ 1) {
145 sprintf(buf
+len
, "%s", (char*) status_string
.value
);
146 len
+= status_string
.length
;
148 gss_release_buffer(&min_stat
, &status_string
);
151 gss_release_buffer(&min_stat
, &status_string
);
153 if (sizeof(buf
) > len
+ 2) {
154 sprintf(buf
+len
, "%s", ". ");
159 /* convert minor status code (underlying routine error) to text */
160 maj_stat
= gss_display_status(&min_stat
, minor_status
,
163 &msg_ctx
, &status_string
);
164 if (maj_stat
== GSS_S_COMPLETE
) {
165 if (sizeof(buf
) > len
+ status_string
.length
) {
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
);
175 fprintf(stderr
, "%s| %s: %s failed: %s\n", LogTime(), PROGRAM
, function
, buf
);
176 fprintf(stdout
, "NA %s failed: %s\n",function
, buf
);
178 fprintf(stderr
, "%s| %s: User not authenticated\n", LogTime(), PROGRAM
);
184 int main(int argc
, char * const argv
[])
186 char buf
[MAX_AUTHTOKEN_LEN
];
190 int opt
, rc
, debug
=0, loging
=0;
191 OM_uint32 ret_flags
=0, spnego_flag
=0;
192 char *service_name
=(char *)"HTTP",*host_name
=NULL
;
194 char *service_principal
= NULL
;
195 OM_uint32 major_status
, minor_status
;
196 gss_ctx_id_t gss_context
= GSS_C_NO_CONTEXT
;
197 gss_name_t client_name
= GSS_C_NO_NAME
;
198 gss_name_t server_name
= GSS_C_NO_NAME
;
199 gss_cred_id_t server_creds
= GSS_C_NO_CREDENTIAL
;
200 gss_cred_id_t delegated_cred
= GSS_C_NO_CREDENTIAL
;
201 gss_buffer_desc service
= GSS_C_EMPTY_BUFFER
;
202 gss_buffer_desc input_token
= GSS_C_EMPTY_BUFFER
;
203 gss_buffer_desc output_token
= GSS_C_EMPTY_BUFFER
;
204 const unsigned char *kerberosToken
= NULL
;
205 size_t kerberosTokenLength
= 0;
206 const unsigned char *spnegoToken
= NULL
;
207 size_t spnegoTokenLength
= 0;
212 while (-1 != (opt
= getopt(argc
, argv
, "dis:h"))) {
221 service_principal
= strdup(optarg
);
224 fprintf(stdout
, "Usage: \n");
225 fprintf(stdout
, "squid_kerb_auth -d [-s SPN]\n");
226 fprintf(stdout
, "SPN = service principal name\n");
227 fprintf(stdout
, "Can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
228 fprintf(stdout
, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
231 fprintf(stderr
, "%s| %s: unknown option: -%c.\n", LogTime(), PROGRAM
, opt
);
235 if (service_principal
&& strcasecmp(service_principal
,"GSS_C_NO_NAME") ) {
236 service
.value
= service_principal
;
237 service
.length
= strlen((char *)service
.value
);
239 host_name
=gethost_name();
241 fprintf(stderr
, "%s| %s: Local hostname could not be determined. Please specify the service principal\n", LogTime(), PROGRAM
);
244 service
.value
= malloc(strlen(service_name
)+strlen(host_name
)+2);
245 snprintf(service
.value
,strlen(service_name
)+strlen(host_name
)+2,"%s@%s",service_name
,host_name
);
246 service
.length
= strlen((char *)service
.value
);
250 if (fgets(buf
, sizeof(buf
)-1, stdin
) == NULL
) {
253 fprintf(stderr
, "%s| %s: fgets() failed! dying..... errno=%d (%s)\n", LogTime(), PROGRAM
, ferror(stdin
),
254 strerror(ferror(stdin
)));
256 exit(1); /* BIIG buffer */
261 c
=memchr(buf
,'\n',sizeof(buf
)-1);
270 fprintf(stderr
, "%s| %s: Oversized message\n", LogTime(), PROGRAM
);
271 fprintf(stdout
, "NA Oversized message\n");
277 fprintf(stderr
, "%s| %s: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM
, buf
?buf
:"NULL",length
);
279 if (buf
[0] == '\0') {
281 fprintf(stderr
, "%s| %s: Invalid request\n", LogTime(), PROGRAM
);
282 fprintf(stdout
, "NA Invalid request\n");
286 if (strlen(buf
) < 2) {
288 fprintf(stderr
, "%s| %s: Invalid request [%s]\n", LogTime(), PROGRAM
, buf
);
289 fprintf(stdout
, "NA Invalid request\n");
293 if ( !strncmp(buf
, "QQ", 2) ) {
294 gss_release_buffer(&minor_status
, &input_token
);
295 gss_release_buffer(&minor_status
, &output_token
);
296 gss_release_buffer(&minor_status
, &service
);
297 gss_release_cred(&minor_status
, &server_creds
);
298 gss_release_cred(&minor_status
, &delegated_cred
);
299 gss_release_name(&minor_status
, &server_name
);
300 gss_release_name(&minor_status
, &client_name
);
301 gss_delete_sec_context(&minor_status
, &gss_context
, NULL
);
303 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
305 free((char *)kerberosToken
);
309 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
311 free((char *)spnegoToken
);
325 if ( !strncmp(buf
, "YR", 2) && !strncmp(buf
, "KK", 2) ) {
327 fprintf(stderr
, "%s| %s: Invalid request [%s]\n", LogTime(), PROGRAM
, buf
);
328 fprintf(stdout
, "NA Invalid request\n");
331 if ( !strncmp(buf
, "YR", 2) ){
332 if (gss_context
!= GSS_C_NO_CONTEXT
)
333 gss_delete_sec_context(&minor_status
, &gss_context
, NULL
);
334 gss_context
= GSS_C_NO_CONTEXT
;
337 if (strlen(buf
) <= 3) {
339 fprintf(stderr
, "%s| %s: Invalid negotiate request [%s]\n", LogTime(), PROGRAM
, buf
);
340 fprintf(stdout
, "NA Invalid negotiate request\n");
344 input_token
.length
= base64_decode_len(buf
+3);
345 input_token
.value
= malloc(input_token
.length
);
347 base64_decode(input_token
.value
,buf
+3,input_token
.length
);
351 if (( rc
=parseNegTokenInit (input_token
.value
,
354 &kerberosTokenLength
))!=0 ){
356 fprintf(stderr
, "%s| %s: parseNegTokenInit failed with rc=%d\n", LogTime(), PROGRAM
, rc
);
358 /* if between 100 and 200 it might be a GSSAPI token and not a SPNEGO token */
359 if ( rc
< 100 || rc
> 199 ) {
361 fprintf(stderr
, "%s| %s: Invalid GSS-SPNEGO query [%s]\n", LogTime(), PROGRAM
, buf
);
362 fprintf(stdout
, "NA Invalid GSS-SPNEGO query\n");
365 if ((input_token
.length
>= sizeof ntlmProtocol
+ 1) &&
366 (!memcmp (input_token
.value
, ntlmProtocol
, sizeof ntlmProtocol
))) {
368 fprintf(stderr
, "%s| %s: received type %d NTLM token\n", LogTime(), PROGRAM
, (int) *((unsigned char *)input_token
.value
+ sizeof ntlmProtocol
));
369 fprintf(stdout
, "NA received type %d NTLM token\n",(int) *((unsigned char *)input_token
.value
+ sizeof ntlmProtocol
));
374 gss_release_buffer(&minor_status
, &input_token
);
375 input_token
.length
=kerberosTokenLength
;
376 input_token
.value
=(void *)kerberosToken
;
380 if ((input_token
.length
>= sizeof ntlmProtocol
+ 1) &&
381 (!memcmp (input_token
.value
, ntlmProtocol
, sizeof ntlmProtocol
))) {
383 fprintf(stderr
, "%s| %s: received type %d NTLM token\n", LogTime(), PROGRAM
, (int) *((unsigned char *)input_token
.value
+ sizeof ntlmProtocol
));
384 fprintf(stdout
, "NA received type %d NTLM token\n",(int) *((unsigned char *)input_token
.value
+ sizeof ntlmProtocol
));
389 if ( service_principal
) {
390 if ( strcasecmp(service_principal
,"GSS_C_NO_NAME") ){
391 major_status
= gss_import_name(&minor_status
, &service
,
392 (gss_OID
) GSS_C_NULL_OID
, &server_name
);
395 server_name
= GSS_C_NO_NAME
;
396 major_status
= GSS_S_COMPLETE
;
399 major_status
= gss_import_name(&minor_status
, &service
,
400 gss_nt_service_name
, &server_name
);
403 if ( check_gss_err(major_status
,minor_status
,"gss_import_name()",debug
,loging
) )
406 major_status
= gss_acquire_cred(&minor_status
, server_name
, GSS_C_INDEFINITE
,
407 GSS_C_NO_OID_SET
, GSS_C_ACCEPT
, &server_creds
,
409 if (check_gss_err(major_status
,minor_status
,"gss_acquire_cred()",debug
,loging
) )
412 major_status
= gss_accept_sec_context(&minor_status
,
416 GSS_C_NO_CHANNEL_BINDINGS
,
425 if (output_token
.length
) {
428 if ((rc
=makeNegTokenTarg (output_token
.value
,
431 &spnegoTokenLength
))!=0 ) {
433 fprintf(stderr
, "%s| %s: makeNegTokenTarg failed with rc=%d\n", LogTime(), PROGRAM
, rc
);
434 fprintf(stdout
, "NA makeNegTokenTarg failed with rc=%d\n",rc
);
438 spnegoToken
= output_token
.value
;
439 spnegoTokenLength
= output_token
.length
;
442 spnegoToken
= output_token
.value
;
443 spnegoTokenLength
= output_token
.length
;
445 token
= malloc(base64_encode_len(spnegoTokenLength
));
448 fprintf(stderr
, "%s| %s: Not enough memory\n", LogTime(), PROGRAM
);
449 fprintf(stdout
, "NA Not enough memory\n");
453 base64_encode(token
,(const char *)spnegoToken
,base64_encode_len(spnegoTokenLength
),spnegoTokenLength
);
455 if (check_gss_err(major_status
,minor_status
,"gss_accept_sec_context()",debug
,loging
) )
457 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
459 fprintf(stderr
, "%s| %s: continuation needed\n", LogTime(), PROGRAM
);
460 fprintf(stdout
, "TT %s\n",token
);
463 gss_release_buffer(&minor_status
, &output_token
);
464 major_status
= gss_display_name(&minor_status
, client_name
, &output_token
,
467 if (check_gss_err(major_status
,minor_status
,"gss_display_name()",debug
,loging
) )
469 fprintf(stdout
, "AF %s %s\n",token
,(char *)output_token
.value
);
471 fprintf(stderr
, "%s| %s: AF %s %s\n", LogTime(), PROGRAM
, token
,(char *)output_token
.value
);
473 fprintf(stderr
, "%s| %s: User %s authenticated\n", LogTime(), PROGRAM
, (char *)output_token
.value
);
476 if (check_gss_err(major_status
,minor_status
,"gss_accept_sec_context()",debug
,loging
) )
478 if (major_status
& GSS_S_CONTINUE_NEEDED
) {
480 fprintf(stderr
, "%s| %s: continuation needed\n", LogTime(), PROGRAM
);
481 fprintf(stdout
, "NA No token to return to continue\n");
484 gss_release_buffer(&minor_status
, &output_token
);
485 major_status
= gss_display_name(&minor_status
, client_name
, &output_token
,
488 if (check_gss_err(major_status
,minor_status
,"gss_display_name()",debug
,loging
) )
491 * Return dummy token AA. May need an extra return tag then AF
493 fprintf(stdout
, "AF %s %s\n","AA==",(char *)output_token
.value
);
495 fprintf(stderr
, "%s| %s: AF %s %s\n", LogTime(), PROGRAM
, "AA==", (char *)output_token
.value
);
497 fprintf(stderr
, "%s| %s: User %s authenticated\n", LogTime(), PROGRAM
, (char *)output_token
.value
);
500 gss_release_buffer(&minor_status
, &input_token
);
501 gss_release_buffer(&minor_status
, &output_token
);
502 gss_release_cred(&minor_status
, &server_creds
);
503 gss_release_cred(&minor_status
, &delegated_cred
);
504 gss_release_name(&minor_status
, &server_name
);
505 gss_release_name(&minor_status
, &client_name
);
507 /* Allocated by parseNegTokenInit, but no matching free function exists.. */
509 free((char *)kerberosToken
);
513 /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
515 free((char *)spnegoToken
);