Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/gss_creds.c
Original file line number Diff line number Diff line change
Expand Up @@ -716,3 +716,58 @@ uint32_t gssntlm_inquire_cred_by_mech(uint32_t *minor_status,
if (cred_usage) *cred_usage = usage;
return GSSERRS(0, GSS_S_COMPLETE);
}

gss_OID_desc gssntlm_neg_flags_oid = {
GSS_NTLMSSP_NEG_FLAGS_OID_LENGTH,
discard_const(GSS_NTLMSSP_NEG_FLAGS_OID_STRING)
};

static uint32_t gssntlm_set_cred_neg_flags(uint32_t *minor_status,
struct gssntlm_cred *cred,
const gss_buffer_t value)
{

if (cred == NULL || value == NULL) {
*minor_status = EINVAL;
return GSS_S_CALL_INACCESSIBLE_READ;
}
if (value->length == 0) {
/* special to reset to library defaults */
if (cred->type == GSSNTLM_CRED_SERVER) {
cred->neg_flags = NTLMSSP_DEFAULT_SERVER_FLAGS;
} else {
cred->neg_flags = NTLMSSP_DEFAULT_CLIENT_FLAGS;
}
} else if (value->length == sizeof(uint32_t)) {
cred->neg_flags = *(uint32_t *)value->value;
} else {
*minor_status = EINVAL;
return GSS_S_FAILURE;
}

*minor_status = 0;
return GSS_S_COMPLETE;
}

uint32_t gssntlm_set_cred_option(uint32_t *minor_status,
gss_cred_id_t *cred_handle,
const gss_OID desired_object,
const gss_buffer_t value)
{
struct gssntlm_cred *cred;

if (minor_status == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE;
*minor_status = 0;

if (cred_handle == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE;
cred = (struct gssntlm_cred *)*cred_handle;

if (desired_object == GSS_C_NO_OID) return GSS_S_CALL_INACCESSIBLE_READ;

if (gss_oid_equal(desired_object, &gssntlm_neg_flags_oid)) {
return gssntlm_set_cred_neg_flags(minor_status, cred, value);
}

*minor_status = EINVAL;
return GSS_S_UNAVAILABLE;
}
10 changes: 10 additions & 0 deletions src/gss_ntlmssp.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ struct gssntlm_cred {
bool creds_in_cache;
} external;
} cred;

/* set cred options provided default flags
* this is currently intentionally not imported/exported
* as it is considered an ephemeral local status
*/
uint32_t neg_flags;
};

struct gssntlm_ctx {
Expand Down Expand Up @@ -453,4 +459,8 @@ uint32_t gssntlm_inquire_attrs_for_mech(uint32_t *minor_status,
gss_OID_set *mech_attrs,
gss_OID_set *known_mech_attrs);

uint32_t gssntlm_set_cred_option(uint32_t *minor_status,
gss_cred_id_t *cred_handle,
const gss_OID desired_object,
const gss_buffer_t value);
#endif /* _GSS_NTLMSSP_H_ */
9 changes: 9 additions & 0 deletions src/gss_sec_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
ctx->gss_flags = req_flags;

ctx->neg_flags = NTLMSSP_DEFAULT_CLIENT_FLAGS;
/* override neg_flags default if requested */
if (cred->neg_flags) {
ctx->neg_flags = cred->neg_flags;
}

/*
* we ignore unsupported flags for now
Expand Down Expand Up @@ -635,6 +639,11 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
ctx->neg_flags = NTLMSSP_DEFAULT_SERVER_FLAGS;
/* Fixme: How do we allow anonymous negotition ? */

/* override neg_flags default if requested */
if (cred->neg_flags) {
ctx->neg_flags = cred->neg_flags;
}

if (gssntlm_sec_lm_ok(ctx)) {
ctx->neg_flags |= NTLMSSP_REQUEST_NON_NT_SESSION_KEY;
ctx->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
Expand Down
9 changes: 9 additions & 0 deletions src/gss_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -452,3 +452,12 @@ OM_uint32 gssspi_mech_invoke(OM_uint32 *minor_status,
return gssntlm_mech_invoke(minor_status, desired_mech, desired_object,
value);
}

OM_uint32 gssspi_set_cred_option(OM_uint32 *minor_status,
gss_cred_id_t *cred_handle,
const gss_OID desired_object,
const gss_buffer_t value)
{
return gssntlm_set_cred_option(minor_status, cred_handle, desired_object,
value);
}
6 changes: 6 additions & 0 deletions src/gssapi_ntlmssp.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ extern "C" {
#define GSS_NTLMSSP_DEBUG_OID_STRING GSS_NTLMSSP_BASE_OID_STRING "\x04"
#define GSS_NTLMSSP_DEBUG_OID_LENGTH GSS_NTLMSSP_BASE_OID_LENGTH + 1

/* Set Default Neg Flags Cred Option OID
* Use this with gss_set_cred_option to provide a set of NEGOTIATE flags
* to override the default selection on context initialization.
*/
#define GSS_NTLMSSP_NEG_FLAGS_OID_STRING GSS_NTLMSSP_BASE_OID_STRING "\x05"
#define GSS_NTLMSSP_NEG_FLAGS_OID_LENGTH GSS_NTLMSSP_BASE_OID_LENGTH + 1


#define GSS_NTLMSSP_CS_DOMAIN "ntlmssp_domain"
Expand Down
255 changes: 255 additions & 0 deletions tests/ntlmssptest.c
Original file line number Diff line number Diff line change
Expand Up @@ -1892,6 +1892,256 @@ int test_gssapi_1(bool user_env_file, bool use_cb, bool no_seal, bool use_cs)
return ret;
}

int inner_setup_channel(gss_cred_id_t cli_cred, gss_ctx_id_t *cli_ctx,
gss_cred_id_t srv_cred, gss_ctx_id_t *srv_ctx,
gss_name_t gss_srvname, int *step)
{
gss_buffer_desc cli_token = { 0 };
gss_buffer_desc srv_token = { 0 };
uint32_t retmin, retmaj;
uint32_t req_flags = 0;
int ret;

*step = 1;
retmaj = gssntlm_init_sec_context(&retmin, cli_cred, cli_ctx,
gss_srvname, GSS_C_NO_OID,
req_flags, 0, GSS_C_NO_CHANNEL_BINDINGS,
GSS_C_NO_BUFFER, NULL, &cli_token,
NULL, NULL);
if (retmaj != GSS_S_CONTINUE_NEEDED) {
print_gss_error("gssntlm_init_sec_context 1 failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

*step = 2;
retmaj = gssntlm_accept_sec_context(&retmin, srv_ctx, srv_cred,
&cli_token, GSS_C_NO_CHANNEL_BINDINGS,
NULL, NULL, &srv_token,
NULL, NULL, NULL);
if (retmaj != GSS_S_CONTINUE_NEEDED) {
print_gss_error("gssntlm_accept_sec_context 1 failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

gss_release_buffer(&retmin, &cli_token);

*step = 3;
retmaj = gssntlm_init_sec_context(&retmin, cli_cred, cli_ctx,
gss_srvname, GSS_C_NO_OID,
req_flags, 0, GSS_C_NO_CHANNEL_BINDINGS,
&srv_token, NULL, &cli_token,
NULL, NULL);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_init_sec_context 2 failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

gss_release_buffer(&retmin, &srv_token);

*step = 4;
retmaj = gssntlm_accept_sec_context(&retmin, srv_ctx, srv_cred,
&cli_token, GSS_C_NO_CHANNEL_BINDINGS,
NULL, NULL, &srv_token,
NULL, NULL, NULL);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_accept_sec_context 2 failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

ret = 0;

done:
gss_release_buffer(&retmin, &cli_token);
gss_release_buffer(&retmin, &srv_token);
return ret;
}

int test_gssapi_neg_flags(void)
{
gss_ctx_id_t cli_ctx = GSS_C_NO_CONTEXT;
gss_ctx_id_t srv_ctx = GSS_C_NO_CONTEXT;
gss_cred_id_t cli_cred = GSS_C_NO_CREDENTIAL;
gss_cred_id_t srv_cred = GSS_C_NO_CREDENTIAL;
gss_OID_desc gssntlm_neg_flags_oid = {
GSS_NTLMSSP_NEG_FLAGS_OID_LENGTH,
discard_const(GSS_NTLMSSP_NEG_FLAGS_OID_STRING)
};
const char *username;
const char *password = "testpassword";
const char *srvname = "test@testserver";
gss_name_t gss_username = NULL;
gss_name_t gss_srvname = NULL;
gss_buffer_desc pwbuf;
gss_buffer_desc nbuf;
gss_buffer_desc value;
uint32_t neg_flags;
uint32_t retmin, retmaj;
int step;
int ret;

setenv("NTLM_USER_FILE", TEST_USER_FILE, 0);
username = getenv("TEST_USER_NAME");

if (username == NULL) {
username = "TESTDOM\\testuser";
}
nbuf.value = discard_const(username);
nbuf.length = strlen(username);
retmaj = gssntlm_import_name(&retmin, &nbuf,
GSS_C_NT_USER_NAME,
&gss_username);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_import_name(username) failed!",
retmaj, retmin);
return EINVAL;
}

pwbuf.value = discard_const(password);
pwbuf.length = strlen(password);
retmaj = gssntlm_acquire_cred_with_password(&retmin,
(gss_name_t)gss_username,
(gss_buffer_t)&pwbuf,
GSS_C_INDEFINITE,
GSS_C_NO_OID_SET,
GSS_C_INITIATE,
&cli_cred, NULL, NULL);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_acquire_cred_with_password failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

nbuf.value = discard_const(srvname);
nbuf.length = strlen(srvname);
retmaj = gssntlm_import_name(&retmin, &nbuf,
GSS_C_NT_HOSTBASED_SERVICE,
&gss_srvname);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_import_name(srvname) failed!",
retmaj, retmin);
return EINVAL;
}

retmaj = gssntlm_acquire_cred(&retmin, (gss_name_t)gss_srvname,
GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
GSS_C_ACCEPT, &srv_cred, NULL, NULL);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_acquire_cred(srvname) failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

ret = inner_setup_channel(cli_cred, &cli_ctx, srv_cred, &srv_ctx,
gss_srvname, &step);
if (ret != 0) {
goto done;
}

gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER);
gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER);

/* test again with different neg flags */
neg_flags = NTLMSSP_NEGOTIATE_128 \
| NTLMSSP_NEGOTIATE_NTLM \
| NTLMSSP_NEGOTIATE_UNICODE;
value.value = &neg_flags;
value.length = sizeof(neg_flags);

retmaj = gssntlm_set_cred_option(&retmin, &cli_cred,
&gssntlm_neg_flags_oid, &value);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_set_cred_option(cli_cred) failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

ret = inner_setup_channel(cli_cred, &cli_ctx, srv_cred, &srv_ctx,
gss_srvname, &step);
if (ret != 0) {
goto done;
}
fprintf(stderr, "1 ");

gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER);
gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER);

/* test again with incompatible neg flags */
neg_flags = NTLMSSP_NEGOTIATE_56;
value.value = &neg_flags;
value.length = sizeof(neg_flags);

retmaj = gssntlm_set_cred_option(&retmin, &srv_cred,
&gssntlm_neg_flags_oid, &value);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_set_cred_option(srv_cred) failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

ret = inner_setup_channel(cli_cred, &cli_ctx, srv_cred, &srv_ctx,
gss_srvname, &step);
if (!(ret == 22 && step == 2)) {
fprintf(stderr, "Expected Negotiataion failure (%d, %d)\n", ret, step);
ret = EINVAL;
goto done;
}
fprintf(stderr, "2 ");

gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER);
gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER);

/* test again with reset flags */
value.value = NULL;
value.length = 0;
retmaj = gssntlm_set_cred_option(&retmin, &cli_cred,
&gssntlm_neg_flags_oid, &value);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_set_cred_option(cli_cred) failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

retmaj = gssntlm_set_cred_option(&retmin, &srv_cred,
&gssntlm_neg_flags_oid, &value);
if (retmaj != GSS_S_COMPLETE) {
print_gss_error("gssntlm_set_cred_option(srv_cred) failed!",
retmaj, retmin);
ret = EINVAL;
goto done;
}

ret = inner_setup_channel(cli_cred, &cli_ctx, srv_cred, &srv_ctx,
gss_srvname, &step);
if (ret != 0) {
goto done;
}
fprintf(stderr, "3 ");

ret = 0;

done:
gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER);
gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER);
gssntlm_release_name(&retmin, &gss_username);
gssntlm_release_name(&retmin, &gss_srvname);
gssntlm_release_cred(&retmin, &cli_cred);
gssntlm_release_cred(&retmin, &srv_cred);
return ret;
}

int test_gssapi_cl(void)
{
gss_ctx_id_t cli_ctx = GSS_C_NO_CONTEXT;
Expand Down Expand Up @@ -3152,6 +3402,11 @@ int main(int argc, const char *argv[])
fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS"));
if (ret) gret += ret;

fprintf(stderr, "Test Negotiate flags variations\n");
ret = test_gssapi_neg_flags();
fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS"));
if (ret) gret++;

done:
ntlm_free_ctx(&ctx);
return gret;
Expand Down