Skip to content

Commit 98cd744

Browse files
committed
Store the full SPN within a server gssntlm_name
The SPN is used to fill the Traget Name Attribute in the Target Info array. This means we need to preserver the SPN as passed to us (via conversion from a GSS Name). This patch adds an spn field to the server union part of gssntlm_name structure. Signed-off-by: Simo Sorce <[email protected]>
1 parent 1cbc124 commit 98cd744

File tree

3 files changed

+173
-25
lines changed

3 files changed

+173
-25
lines changed

src/gss_names.c

+78-24
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2013 Simo Sorce <[email protected]>, see COPYING for license */
1+
/* Copyright 2013-2022 Simo Sorce <[email protected]>, see COPYING for license */
22

33
#define _GNU_SOURCE
44

@@ -266,9 +266,6 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
266266
gss_OID input_name_type,
267267
gss_name_t *output_name)
268268
{
269-
char hostname[HOST_NAME_MAX + 1] = { 0 };
270-
char struid[12] = { 0 };
271-
uid_t uid;
272269
struct gssntlm_name *name = NULL;
273270
uint32_t retmaj;
274271
uint32_t retmin;
@@ -291,30 +288,74 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
291288

292289
if (gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE) ||
293290
gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE_X)) {
291+
char *spn = NULL;
292+
char *p = NULL;
294293

295294
name->type = GSSNTLM_NAME_SERVER;
296295

297-
retmaj = string_split(&retmin, '@',
298-
input_name_buffer->value,
299-
input_name_buffer->length,
300-
NULL, &name->data.server.name);
301-
if ((retmaj == GSS_S_COMPLETE) ||
302-
(retmaj != GSS_S_UNAVAILABLE)) {
303-
goto done;
296+
if (input_name_buffer->length > 0) {
297+
spn = strndup(input_name_buffer->value, input_name_buffer->length);
298+
if (!spn) {
299+
set_GSSERR(ENOMEM);
300+
goto done;
301+
}
302+
p = memchr(spn, '@', input_name_buffer->length);
303+
if (p && input_name_buffer->length == 1) {
304+
free(spn);
305+
spn = p = NULL;
306+
}
304307
}
305308

306-
/* no seprator, assume only service is provided and try to source
307-
* the local host name */
308-
retmin = gethostname(hostname, HOST_NAME_MAX);
309-
if (retmin) {
310-
set_GSSERR(retmin);
311-
goto done;
312-
}
313-
hostname[HOST_NAME_MAX] = '\0';
314-
name->data.server.name = strdup(hostname);
315-
if (!name->data.server.name) {
316-
set_GSSERR(ENOMEM);
309+
if (p) {
310+
/* Windows expects a SPN not a GSS Name */
311+
if (p != spn) {
312+
*p = '/';
313+
name->data.server.spn = spn;
314+
spn = NULL;
315+
}
316+
p += 1;
317+
name->data.server.name = strdup(p);
318+
if (!name->data.server.name) {
319+
free(spn);
320+
set_GSSERR(ENOMEM);
321+
goto done;
322+
}
323+
} else {
324+
char hostname[HOST_NAME_MAX + 1] = { 0 };
325+
size_t l, r;
326+
/* no seprator, assume only service is provided and try to
327+
* source the local host name */
328+
retmin = gethostname(hostname, HOST_NAME_MAX);
329+
if (retmin) {
330+
free(spn);
331+
set_GSSERR(retmin);
332+
goto done;
333+
}
334+
hostname[HOST_NAME_MAX] = '\0';
335+
if (spn != NULL) {
336+
/* spn = <service> + </> + <hostname> + <\0> */
337+
l = strlen(spn) + 1 + strlen(hostname) + 1;
338+
name->data.server.spn = malloc(l);
339+
if (!name->data.server.spn) {
340+
free(spn);
341+
set_GSSERR(ENOMEM);
342+
goto done;
343+
}
344+
r = snprintf(name->data.server.spn, l, "%s/%s", spn, hostname);
345+
if (r != l - 1) {
346+
free(spn);
347+
set_GSSERR(ENOMEM);
348+
goto done;
349+
}
350+
}
351+
name->data.server.name = strdup(hostname);
352+
if (!name->data.server.name) {
353+
free(spn);
354+
set_GSSERR(ENOMEM);
355+
goto done;
356+
}
317357
}
358+
free(spn);
318359
set_GSSERRS(0, GSS_S_COMPLETE);
319360

320361
} else if (gss_oid_equal(input_name_type, GSS_C_NT_USER_NAME)) {
@@ -326,13 +367,16 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
326367
&name->data.user.domain,
327368
&name->data.user.name);
328369
} else if (gss_oid_equal(input_name_type, GSS_C_NT_MACHINE_UID_NAME)) {
370+
uid_t uid;
329371

330372
name->type = GSSNTLM_NAME_USER;
331373
name->data.user.domain = NULL;
332374

333375
uid = *(uid_t *)input_name_buffer->value;
334376
retmaj = uid_to_name(&retmin, uid, &name->data.user.name);
335377
} else if (gss_oid_equal(input_name_type, GSS_C_NT_STRING_UID_NAME)) {
378+
char struid[12] = { 0 };
379+
uid_t uid;
336380

337381
name->type = GSSNTLM_NAME_USER;
338382
name->data.user.domain = NULL;
@@ -454,7 +498,7 @@ void gssntlm_release_attrs(struct gssntlm_name_attribute **attrs)
454498

455499
int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst)
456500
{
457-
char *dom = NULL, *usr = NULL, *srv = NULL;
501+
char *dom = NULL, *usr = NULL, *spn = NULL, *srv = NULL;
458502
int ret;
459503
dst->type = src->type;
460504
switch (src->type) {
@@ -480,6 +524,14 @@ int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst)
480524
dst->data.user.name = usr;
481525
break;
482526
case GSSNTLM_NAME_SERVER:
527+
if (src->data.server.spn) {
528+
spn = strdup(src->data.server.spn);
529+
if (!spn) {
530+
ret = ENOMEM;
531+
goto done;
532+
}
533+
}
534+
dst->data.server.spn = spn;
483535
if (src->data.server.name) {
484536
srv = strdup(src->data.server.name);
485537
if (!srv) {
@@ -499,6 +551,7 @@ int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst)
499551
if (ret) {
500552
safefree(dom);
501553
safefree(usr);
554+
safefree(spn);
502555
safefree(srv);
503556
}
504557
return ret;
@@ -560,6 +613,7 @@ void gssntlm_int_release_name(struct gssntlm_name *name)
560613
safefree(name->data.user.name);
561614
break;
562615
case GSSNTLM_NAME_SERVER:
616+
safefree(name->data.server.spn);
563617
safefree(name->data.server.name);
564618
break;
565619
}
@@ -635,7 +689,7 @@ uint32_t gssntlm_display_name(uint32_t *minor_status,
635689
}
636690
break;
637691
case GSSNTLM_NAME_SERVER:
638-
out->value = strdup(in->data.server.name);
692+
out->value = strdup(in->data.server.spn);
639693
if (!out->value) {
640694
set_GSSERR(ENOMEM);
641695
goto done;

src/gss_ntlmssp.h

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct gssntlm_name {
5959
char *name;
6060
} user;
6161
struct {
62+
char *spn;
6263
char *name;
6364
} server;
6465
} data;

tests/ntlmssptest.c

+94-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1-
/* Copyright 2013 Simo Sorce <[email protected]>, see COPYING for license */
1+
/* Copyright 2013-2022 Simo Sorce <[email protected]>, see COPYING for license */
22

33
#include <ctype.h>
44
#include <errno.h>
5+
#include <limits.h>
56
#include <stdio.h>
67
#include <string.h>
78
#include <stdlib.h>
89
#include <time.h>
10+
#include <unistd.h>
911

1012
#include "config.h"
1113

14+
#ifndef HOST_NAME_MAX
15+
#include <sys/param.h>
16+
#define HOST_NAME_MAX MAXHOSTNAMELEN
17+
#endif
18+
1219
#include "../src/gssapi_ntlmssp.h"
1320
#include "../src/gss_ntlmssp.h"
1421

@@ -2790,6 +2797,87 @@ int test_import_name(void)
27902797
return ret;
27912798
}
27922799

2800+
int test_hostbased_name(void)
2801+
{
2802+
char hostname[HOST_NAME_MAX + 1] = { 0 };
2803+
struct {
2804+
const char *input;
2805+
const char *name;
2806+
const char *spn_svc;
2807+
const char *spn_name;
2808+
size_t spn_svc_len;
2809+
} hostbased_test[] = {
2810+
{ "HTTP", hostname, "HTTP/", hostname, 5 },
2811+
{ "[email protected]", "foo.bar", "HTTP/", "foo.bar", 5 },
2812+
{ "@foo.bar", "foo.bar", NULL, NULL, 0 },
2813+
{ "@", hostname, NULL, NULL, 0 },
2814+
{ "", hostname, NULL, NULL, 0 },
2815+
{ NULL, NULL, NULL, NULL, 0 }
2816+
};
2817+
int ret = 0;
2818+
2819+
/* get hostname to verify results */
2820+
ret = gethostname(hostname, HOST_NAME_MAX);
2821+
if (ret) {
2822+
fprintf(stderr, "Test: test_hostbased_name failed to get hostname\n");
2823+
}
2824+
2825+
for (int i = 0; hostbased_test[i].input != NULL; i++) {
2826+
struct gssntlm_name *gss_host_name = NULL;
2827+
gss_buffer_desc host_name;
2828+
uint32_t retmin, retmaj;
2829+
bool failed = false;
2830+
2831+
host_name.value = discard_const(hostbased_test[i].input);
2832+
host_name.length = strlen(host_name.value);
2833+
2834+
retmaj = gssntlm_import_name(&retmin, &host_name,
2835+
GSS_C_NT_HOSTBASED_SERVICE,
2836+
(gss_name_t *)&gss_host_name);
2837+
if (retmaj == GSS_S_COMPLETE) {
2838+
if ((gss_host_name->type != GSSNTLM_NAME_SERVER) ||
2839+
(strcmp(hostbased_test[i].name,
2840+
gss_host_name->data.server.name) != 0)) {
2841+
failed = true;
2842+
}
2843+
if (hostbased_test[i].spn_svc_len != 0) {
2844+
if ((strncmp(hostbased_test[i].spn_svc,
2845+
gss_host_name->data.server.spn,
2846+
hostbased_test[i].spn_svc_len) != 0) ||
2847+
(strcmp(hostbased_test[i].spn_name,
2848+
gss_host_name->data.server.spn +
2849+
hostbased_test[i].spn_svc_len) != 0)) {
2850+
failed = true;
2851+
}
2852+
}
2853+
} else {
2854+
failed = true;
2855+
}
2856+
2857+
if (failed) {
2858+
fprintf(stderr, "gssntlm_import_name(%s) failed!\n",
2859+
hostbased_test[i].input);
2860+
fprintf(stderr, "Expected: [%s%s]\n",
2861+
hostbased_test[i].spn_svc, hostbased_test[i].spn_name);
2862+
if (gss_host_name) {
2863+
fprintf(stderr, "Obtained: [%s]+[%s]\n",
2864+
gss_host_name->data.server.spn,
2865+
gss_host_name->data.server.name);
2866+
}
2867+
if (retmaj != GSS_S_COMPLETE) {
2868+
print_gss_error("Function returned error.", retmaj, retmin);
2869+
}
2870+
fflush(stderr);
2871+
2872+
ret++;
2873+
}
2874+
2875+
gssntlm_release_name(&retmin, (gss_name_t *)&gss_host_name);
2876+
}
2877+
2878+
return ret;
2879+
}
2880+
27932881
int test_debug(void)
27942882
{
27952883
char *test_env;
@@ -3059,6 +3147,11 @@ int main(int argc, const char *argv[])
30593147
fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS"));
30603148
if (ret) gret += ret;
30613149

3150+
fprintf(stderr, "Test importing different hostbased name forms\n");
3151+
ret = test_hostbased_name();
3152+
fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS"));
3153+
if (ret) gret += ret;
3154+
30623155
done:
30633156
ntlm_free_ctx(&ctx);
30643157
return gret;

0 commit comments

Comments
 (0)