Skip to content

Commit 3ed44df

Browse files
author
Sean Noonan
committed
Work properly when run on non-standard ports
1 parent 117fbd8 commit 3ed44df

File tree

1 file changed

+78
-95
lines changed

1 file changed

+78
-95
lines changed

ngx_http_auth_spnego_module.c

Lines changed: 78 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,14 @@
88
#include <ngx_config.h>
99
#include <ngx_core.h>
1010
#include <ngx_http.h>
11-
/*
12-
#include <ngx_string.h>
13-
*/
1411

1512
#include <gssapi/gssapi.h>
16-
13+
#include <gssapi/gssapi_krb5.h>
1714
#include <krb5.h>
1815

1916
#define krb5_get_err_text(context,code) error_message(code)
2017

21-
/* #include <spnegohelp.h> */
22-
int parseNegTokenInit (const unsigned char * negTokenInit,
23-
size_t negTokenInitLength,
24-
const unsigned char ** kerberosToken,
25-
size_t * kerberosTokenLength);
26-
int makeNegTokenTarg (const unsigned char * kerberosToken,
27-
size_t kerberosTokenLength,
28-
const unsigned char ** negTokenTarg,
29-
size_t * negTokenTargLength);
18+
#include <spnegohelp.h>
3019

3120
/* Module handler */
3221
static ngx_int_t ngx_http_auth_spnego_handler(ngx_http_request_t*);
@@ -48,20 +37,13 @@ get_gss_error(ngx_pool_t *p,
4837
size_t len;
4938
ngx_str_t str;
5039

51-
/* ngx_fubarprintf... what a hack... %Z inserts '\0' */
40+
/* %Z inserts '\0' */
5241
ngx_snprintf((u_char *) buf, sizeof(buf), "%s: %Z", prefix);
5342
len = ngx_strlen(buf);
5443
do {
55-
maj_stat = gss_display_status (&min_stat,
56-
error_status,
57-
GSS_C_MECH_CODE,
58-
GSS_C_NO_OID,
59-
&msg_ctx,
60-
&status_string);
44+
maj_stat = gss_display_status (&min_stat, error_status, GSS_C_MECH_CODE,
45+
GSS_C_NO_OID, &msg_ctx, &status_string);
6146
if (sizeof(buf) > len + status_string.length + 1) {
62-
/*
63-
sprintf(buf, "%s:", (char*) status_string.value);
64-
*/
6547
ngx_sprintf((u_char *) buf+len, "%s:%Z", (char*) status_string.value);
6648
len += ( status_string.length + 1);
6749
}
@@ -141,6 +123,8 @@ static ngx_command_t ngx_http_auth_spnego_commands[] = {
141123
offsetof(ngx_http_auth_spnego_loc_conf_t, fqun),
142124
NULL },
143125

126+
/* TODO add support for specifying auth_gss_spn explicitely */
127+
144128
ngx_null_command
145129
};
146130

@@ -162,7 +146,6 @@ static ngx_http_module_t ngx_http_auth_spnego_module_ctx = {
162146

163147
/* Module Definition */
164148

165-
/* really ngx_module_s /shrug */
166149
ngx_module_t ngx_http_auth_spnego_module = {
167150
/* ngx_uint_t ctx_index, index, spare{0-3}, version; */
168151
NGX_MODULE_V1, /* 0, 0, 0, 0, 0, 0, 1 */
@@ -193,13 +176,6 @@ ngx_http_auth_spnego_create_loc_conf(ngx_conf_t *cf)
193176
conf->protect = NGX_CONF_UNSET;
194177
conf->fqun = NGX_CONF_UNSET;
195178

196-
/* temporary "debug" */
197-
#if (NGX_DEBUG)
198-
ngx_conf_log_error(NGX_LOG_INFO, cf, 0,
199-
"auth_spnego: allocated loc_conf_t (0x%p)", conf);
200-
#endif
201-
/* TODO find out if there is way to enable it only in debug mode */
202-
203179
return conf;
204180
}
205181

@@ -220,7 +196,6 @@ ngx_http_auth_spnego_merge_loc_conf(ngx_conf_t *cf,
220196

221197
ngx_conf_merge_off_value(conf->fqun, prev->fqun, 0);
222198

223-
/* TODO make it only shout in debug */
224199
#if (NGX_DEBUG)
225200
ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "auth_spnego: protect = %i",
226201
conf->protect);
@@ -261,26 +236,24 @@ ngx_http_auth_spnego_negotiate_headers(ngx_http_request_t *r,
261236
ngx_str_t *token,
262237
ngx_http_auth_spnego_loc_conf_t *alcf)
263238
{
239+
/* The string handling here is inconsistent. Should we be including the NUL
240+
* or not? My feeling is not, given that these are all ngx_str_t with defined
241+
* length.
242+
*
243+
* TODO: Fix string lengths to be consistent about NUL termination
244+
*/
264245
ngx_str_t value = ngx_null_string;
265-
ngx_str_t value2 = ngx_null_string;
266246

267247
if (token == NULL) {
268248
value.len = sizeof("Negotiate") - 1;
269249
value.data = (u_char *) "Negotiate";
270-
value2.len = sizeof("Basic realm=\"\"") + alcf->realm.len;
271-
value2.data = ngx_pcalloc(r->pool, value2.len + 1);
272-
if (value2.data == NULL) {
273-
return NGX_ERROR;
274-
}
275-
ngx_snprintf(value2.data, value2.len + 1, "Basic realm=\"%V\"%Z",
276-
&alcf->realm);
277250
} else {
278-
value.len = sizeof("Negotiate") + token->len;
279-
value.data = ngx_pcalloc(r->pool, value.len + 1);
251+
value.len = sizeof("Negotiate") + token->len; //space accounts for \0
252+
value.data = ngx_pcalloc(r->pool, value.len);
280253
if (value.data == NULL) {
281254
return NGX_ERROR;
282255
}
283-
ngx_snprintf(value.data, value.len + 1, "Negotiate %V", token);
256+
ngx_snprintf(value.data, value.len, "Negotiate %V", token);
284257
}
285258

286259
r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
@@ -295,7 +268,15 @@ ngx_http_auth_spnego_negotiate_headers(ngx_http_request_t *r,
295268
r->headers_out.www_authenticate->value.len = value.len;
296269
r->headers_out.www_authenticate->value.data = value.data;
297270

271+
/* Basic auth */
298272
if (token == NULL) {
273+
ngx_str_t value2 = ngx_null_string;
274+
value2.len = sizeof("Basic realm=\"\"") - 1 + alcf->realm.len;
275+
value2.data = ngx_pcalloc(r->pool, value2.len);
276+
if (value2.data == NULL) {
277+
return NGX_ERROR;
278+
}
279+
ngx_snprintf(value2.data, value2.len, "Basic realm=\"%V\"", &alcf->realm);
299280
r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
300281
if (r->headers_out.www_authenticate == NULL) {
301282
return NGX_ERROR;
@@ -314,7 +295,6 @@ ngx_http_auth_spnego_negotiate_headers(ngx_http_request_t *r,
314295
return NGX_OK;
315296
}
316297

317-
/* sort of like ngx_http_auth_basic_user ... except we store in ctx_t? */
318298
ngx_int_t
319299
ngx_http_auth_spnego_token(ngx_http_request_t *r,
320300
ngx_http_auth_spnego_ctx_t *ctx)
@@ -566,32 +546,22 @@ ngx_http_auth_spnego_auth_user_gss(ngx_http_request_t *r,
566546
{
567547
static unsigned char ntlmProtocol [] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
568548

569-
/*
570-
nginx stuff
571-
*/
549+
/* nginx stuff */
572550
ngx_str_t host_name;
573551
ngx_int_t ret = NGX_DECLINED;
574552
int rc;
575553
int spnego_flag = 0;
576554
char *p;
577-
/*
578-
kerberos stuff
579-
*/
555+
556+
/* kerberos stuff */
580557
krb5_context krb_ctx = NULL;
581558
char *ktname = NULL;
582559
/* ngx_str_t kerberosToken; ? */
583560
unsigned char *kerberosToken = NULL;
584561
size_t kerberosTokenLength = 0;
585-
/* this izgotten from de-SPNEGGING original token...
586-
and put into gss_accept_sec_context...
587-
silly...
588-
*/
589562
ngx_str_t spnegoToken = ngx_null_string;
590-
/* unsigned char *spnegoToken = NULL ;
591-
size_t spnegoTokenLength = 0; */
592-
/*
593-
gssapi stuff
594-
*/
563+
564+
/* gssapi stuff */
595565
OM_uint32 major_status, minor_status, minor_status2;
596566
gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
597567
gss_name_t my_gss_name = GSS_C_NO_NAME;
@@ -603,19 +573,16 @@ ngx_http_auth_spnego_auth_user_gss(ngx_http_request_t *r,
603573
OM_uint32 ret_flags = 0;
604574
gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
605575

606-
/* first, see if there is a point in runing */
607-
/* ctx = ngx_http_get_module_ctx(r, ngx_http_auth_spnego_module); */
608-
/* this really shouldn't 'eppen */
609-
if (!ctx || ctx->token.len == 0) {
576+
if (NULL == ctx || ctx->token.len == 0)
610577
return ret;
611-
}
612-
/* on with the copy cat show */
578+
613579
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
614-
"GSSAPI authorizing");
580+
"GSSAPI authorizing");
615581

616582
krb5_init_context(&krb_ctx);
617583

618-
ktname = (char *) ngx_pcalloc(r->pool, sizeof("KRB5_KTNAME=")+alcf->keytab.len);
584+
ktname = (char *) ngx_pcalloc(r->pool, sizeof("KRB5_KTNAME=") +
585+
alcf->keytab.len);
619586
if (ktname == NULL) {
620587
ret = NGX_ERROR;
621588
goto end;
@@ -627,48 +594,64 @@ ngx_http_auth_spnego_auth_user_gss(ngx_http_request_t *r,
627594
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
628595
"Use keytab %V", &alcf->keytab);
629596

630-
/* TODECIDE: wherefrom use the hostname value for the service name? */
597+
/* The required logic here is to choose between these options:
598+
* 1. Use the service as specified in the config? Not yet implemented
599+
* 2. Use the service as expected by the client? (gss_OID)gss_nt_krb5_name
600+
* 3. Allow the library to canonicalize the name? GSS_C_NT_HOSTBASED_SERVICE */
631601
host_name = r->headers_in.host->value;
632-
/* for now using the name client thinks... */
633-
service.length = alcf->srvcname.len + host_name.len + 2;
634-
/* @ vel / */
602+
u_char *port_start = (u_char *)ngx_strchr(host_name.data, ':');
603+
uint real_host_name_len = 0;
604+
if (NULL == port_start) { /* no port number specified */
605+
service.length = alcf->srvcname.len + host_name.len + 2;
606+
real_host_name_len = host_name.len;
607+
} else { /* port number included, strip it */
608+
service.length = alcf->srvcname.len + (port_start - host_name.data - 1) + 2;
609+
real_host_name_len = (port_start - host_name.data);
610+
}
611+
635612
service.value = ngx_palloc(r->pool, service.length);
636613
if (service.value == NULL) {
637614
ret = NGX_ERROR;
638615
goto end;
639616
}
640-
ngx_snprintf(service.value, service.length, "%V@%V%Z",
641-
&alcf->srvcname, &host_name);
642617

643-
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
644-
"Use service principal %V/%V", &alcf->srvcname, &host_name);
618+
ngx_snprintf(service.value, service.length, "%V/%V", &alcf->srvcname,
619+
&host_name);
620+
ngx_snprintf((u_char *)service.value + service.length, 1, "%Z");
621+
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
622+
"Using service principal: %s", service.value);
645623

646624
major_status = gss_import_name(&minor_status, &service,
647-
GSS_C_NT_HOSTBASED_SERVICE, &my_gss_name);
625+
(gss_OID)gss_nt_krb5_name, &my_gss_name);
626+
// GSS_C_NT_HOSTBASED_SERVICE, &my_gss_name);
648627
if (GSS_ERROR(major_status)) {
649-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
650-
"%s Used service principal: %s",
651-
get_gss_error(r->pool, minor_status,
652-
"gss_import_name() failed for service principal"),
653-
(unsigned char *)service.value);
654-
ret = NGX_ERROR;
655-
goto end;
628+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
629+
"%s Used service principal: %s ", get_gss_error(r->pool, minor_status,
630+
"gss_import_name() failed for service principal"),
631+
(u_char *)service.value);
632+
ret = NGX_ERROR;
633+
goto end;
656634
}
657635

658-
major_status = gss_acquire_cred(&minor_status,
659-
my_gss_name,
660-
GSS_C_INDEFINITE,
661-
GSS_C_NO_OID_SET,
662-
GSS_C_ACCEPT,
663-
&my_gss_creds,
664-
NULL,
665-
NULL);
636+
/* Log the credentials we plan to use */
637+
gss_buffer_desc human_readable_gss_name = GSS_C_EMPTY_BUFFER;
638+
major_status = gss_display_name(&minor_status, my_gss_name,
639+
&human_readable_gss_name, NULL);
640+
641+
if (GSS_ERROR(major_status)) {
642+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s", get_gss_error(
643+
r->pool, minor_status, "gss_display_name() failed"));
644+
}
645+
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "my_gss_name %s",
646+
human_readable_gss_name.value);
647+
648+
/* Obtain credentials */
649+
major_status = gss_acquire_cred(&minor_status, my_gss_name, GSS_C_INDEFINITE,
650+
GSS_C_NO_OID_SET, GSS_C_ACCEPT, &my_gss_creds, NULL, NULL);
666651
if (GSS_ERROR(major_status)) {
667652
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
668-
"%s Used service principal: %s",
669-
get_gss_error(r->pool, minor_status,
670-
"gss_acquire_cred() failed"),
671-
(unsigned char *)service.value);
653+
"%s Used service principal: %s", get_gss_error(r->pool, minor_status,
654+
"gss_acquire_cred() failed"), (unsigned char *)service.value);
672655
ret = NGX_ERROR;
673656
goto end;
674657
}

0 commit comments

Comments
 (0)