Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Implement support for anonymous inititators #27

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
153 changes: 84 additions & 69 deletions src/gss_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,55 @@ uint32_t gssntlm_cli_auth(uint32_t *minor_status,
struct ntlm_buffer auth_mic = { NULL, 16 };
uint8_t micbuf[16];
struct ntlm_buffer mic = { micbuf, 16 };
char *username;
char *domain;
bool ext_sec;
bool add_mic = false;
bool key_exch;
uint32_t retmaj;
uint32_t retmin;

ext_sec = (in_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY);

switch (cred->type) {

case GSSNTLM_CRED_EXTERNAL:
retmin = external_cli_auth(ctx, cred, in_flags, input_chan_bindings);
if (retmin) {
set_GSSERR(retmin);
goto done;
}
set_GSSERRS(0, GSS_S_COMPLETE);
return GSSERR();

case GSSNTLM_CRED_ANON:

/* Anonymous auth, empty responses */
if (!(ctx->gss_flags & GSS_C_ANON_FLAG)) {
set_GSSERR(EINVAL);
goto done;
}

domain = NULL;
username = NULL;
memset(&nt_chal_resp, 0, sizeof(nt_chal_resp));
lm_chal_resp.data = malloc(1);
if (!lm_chal_resp.data) {
set_GSSERR(ENOMEM);
goto done;
}
lm_chal_resp.data[0] = 0;
lm_chal_resp.length = 1;

memset(key_exchange_key.data, 0, 16);
break;

case GSSNTLM_CRED_USER:

if (ctx->gss_flags & GSS_C_ANON_FLAG) {
/* Anonymous auth, empty responses */
memset(&nt_chal_resp, 0, sizeof(nt_chal_resp));
lm_chal_resp.data = malloc(1);
if (!lm_chal_resp.data) {
set_GSSERR(ENOMEM);
goto done;
}
lm_chal_resp.data[0] = 0;
lm_chal_resp.length = 1;
domain = cred->cred.user.user.data.user.domain;
username = cred->cred.user.user.data.user.name;

} else if (gssntlm_sec_v2_ok(ctx)) {
if (gssntlm_sec_v2_ok(ctx)) {

/* ### NTLMv2 ### */
uint8_t client_chal[8];
Expand Down Expand Up @@ -111,9 +139,7 @@ uint32_t gssntlm_cli_auth(uint32_t *minor_status,

/* NTLMv2 Key */
retmin = NTOWFv2(ctx->ntlm, &cred->cred.user.nt_hash,
cred->cred.user.user.data.user.name,
cred->cred.user.user.data.user.domain,
&ntlmv2_key);
username, domain, &ntlmv2_key);
if (retmin) {
set_GSSERR(retmin);
goto done;
Expand Down Expand Up @@ -160,7 +186,6 @@ uint32_t gssntlm_cli_auth(uint32_t *minor_status,
struct ntlm_buffer cli_chal = { client_chal, 8 };
struct ntlm_key session_base_key = { .length = 16 };
bool NoLMResponseNTLMv1 = !gssntlm_sec_lm_ok(ctx);
bool ext_sec;

nt_chal_resp.length = 24;
nt_chal_resp.data = calloc(1, nt_chal_resp.length);
Expand All @@ -178,8 +203,6 @@ uint32_t gssntlm_cli_auth(uint32_t *minor_status,
goto done;
}

ext_sec = (in_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY);

retmin = ntlm_compute_nt_response(&cred->cred.user.nt_hash,
ext_sec, ctx->server_chal,
client_chal, &nt_chal_resp);
Expand Down Expand Up @@ -219,77 +242,69 @@ uint32_t gssntlm_cli_auth(uint32_t *minor_status,
}
}

key_exch = (in_flags & NTLMSSP_NEGOTIATE_KEY_EXCH);

retmin = ntlm_exported_session_key(&key_exchange_key, key_exch,
&ctx->exported_session_key);
if (retmin) {
set_GSSERR(retmin);
goto done;
}
break;

if (key_exch) {
retmin = ntlm_encrypted_session_key(&key_exchange_key,
&ctx->exported_session_key,
&encrypted_random_session_key);
if (retmin) {
set_GSSERR(retmin);
goto done;
}
}
default:
set_GSSERR(ERR_NOUSRCRED);
goto done;
}

/* in_flags all verified, assign as current flags */
ctx->neg_flags |= in_flags;
key_exch = (in_flags & NTLMSSP_NEGOTIATE_KEY_EXCH);

enc_sess_key.data = encrypted_random_session_key.data;
enc_sess_key.length = encrypted_random_session_key.length;
retmin = ntlm_exported_session_key(&key_exchange_key, key_exch,
&ctx->exported_session_key);
if (retmin) {
set_GSSERR(retmin);
goto done;
}

retmin = ntlm_encode_auth_msg(ctx->ntlm, ctx->neg_flags,
&lm_chal_resp, &nt_chal_resp,
cred->cred.user.user.data.user.domain,
cred->cred.user.user.data.user.name,
ctx->workstation, &enc_sess_key,
add_mic ? &auth_mic : NULL,
&ctx->auth_msg);
if (key_exch) {
retmin = ntlm_encrypted_session_key(&key_exchange_key,
&ctx->exported_session_key,
&encrypted_random_session_key);
if (retmin) {
set_GSSERR(retmin);
goto done;
}
}

/* Now we need to calculate the MIC, because the MIC is part of the
* message it protects, ntlm_encode_auth_msg() always add a zeroeth
* buffer, however it returns in data_mic the pointer to the actual
* area in the auth_msg that points at the mic, so we can backfill */
if (add_mic) {
retmin = ntlm_mic(&ctx->exported_session_key, &ctx->nego_msg,
&ctx->chal_msg, &ctx->auth_msg, &mic);
if (retmin) {
set_GSSERR(retmin);
goto done;
}
/* now that we have the mic, copy it into the auth message */
memcpy(auth_mic.data, mic.data, 16);
/* in_flags all verified, assign as current flags */
ctx->neg_flags |= in_flags;

/* Make sure SPNEGO gets to know it has to add mechlistMIC too */
ctx->int_flags |= NTLMSSP_CTX_FLAG_AUTH_WITH_MIC;
}
enc_sess_key.data = encrypted_random_session_key.data;
enc_sess_key.length = encrypted_random_session_key.length;

set_GSSERRS(0, GSS_S_COMPLETE);
break;
retmin = ntlm_encode_auth_msg(ctx->ntlm, ctx->neg_flags,
&lm_chal_resp, &nt_chal_resp,
domain, username,
ctx->workstation, &enc_sess_key,
add_mic ? &auth_mic : NULL,
&ctx->auth_msg);
if (retmin) {
set_GSSERR(retmin);
goto done;
}

case GSSNTLM_CRED_EXTERNAL:
retmin = external_cli_auth(ctx, cred, in_flags, input_chan_bindings);
/* Now we need to calculate the MIC, because the MIC is part of the
* message it protects, ntlm_encode_auth_msg() always add a zeroeth
* buffer, however it returns in data_mic the pointer to the actual
* area in the auth_msg that points at the mic, so we can backfill */
if (add_mic) {
retmin = ntlm_mic(&ctx->exported_session_key, &ctx->nego_msg,
&ctx->chal_msg, &ctx->auth_msg, &mic);
if (retmin) {
set_GSSERR(retmin);
goto done;
}
set_GSSERRS(0, GSS_S_COMPLETE);
break;
/* now that we have the mic, copy it into the auth message */
memcpy(auth_mic.data, mic.data, 16);

default:
set_GSSERR(ERR_NOUSRCRED);
/* Make sure SPNEGO gets to know it has to add mechlistMIC too */
ctx->int_flags |= NTLMSSP_CTX_FLAG_AUTH_WITH_MIC;
}

set_GSSERRS(0, GSS_S_COMPLETE);

done:
ntlm_free_buffer_data(&client_target_info);
ntlm_free_buffer_data(&nt_chal_resp);
Expand Down
8 changes: 8 additions & 0 deletions src/gss_creds.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,14 @@ uint32_t gssntlm_acquire_cred_from(uint32_t *minor_status,
}

if (cred_usage == GSS_C_INITIATE) {
if (name != NULL && name->type == GSSNTLM_NAME_ANON) {
cred->type = GSSNTLM_CRED_ANON;
cred->cred.anon.dummy = 0;
retmin = 0;
retmaj = 0;
goto done;
}

if (name != NULL && name->type != GSSNTLM_NAME_USER) {
set_GSSERRS(ERR_NOUSRNAME, GSS_S_BAD_NAMETYPE);
goto done;
Expand Down
3 changes: 3 additions & 0 deletions src/gss_names.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
/* TODO: check mech_type == gssntlm_oid */
if (mech_type == GSS_C_NO_OID) {
return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
} else if (!gss_oid_equal(mech_type, &gssntlm_oid)) {
return GSSERRS(ERR_BADARG, GSS_S_BAD_MECH);
}

name = calloc(1, sizeof(struct gssntlm_name));
Expand Down Expand Up @@ -759,6 +761,7 @@ static uint32_t make_ma_oid_set(uint32_t *minor_status, gss_OID_set *ma_set,
GSS_C_MA_OOS_DET,
GSS_C_MA_CBINDINGS,
GSS_C_MA_CTX_TRANS,
GSS_C_MA_AUTH_INIT_ANON,
NULL
};
uint32_t maj = 0;
Expand Down
13 changes: 13 additions & 0 deletions src/gss_ntlmssp.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,16 @@ int gssntlm_get_lm_compatibility_level(void)
/* use 3 by default for better compatibility */
return 3;
}

bool gssntlm_is_anonymous_allowed(void)
{
const char *envvar;

envvar = getenv("NTLM_ALLOW_ANONYMOUS");
if (envvar != NULL) {
return (atoi(envvar) > 0);
}

/* Not allowed by default */
return false;
}
1 change: 1 addition & 0 deletions src/gss_ntlmssp.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ uint32_t gssntlm_context_is_valid(struct gssntlm_ctx *ctx,
time_t *time_now);

int gssntlm_get_lm_compatibility_level(void);
bool gssntlm_is_anonymous_allowed(void);

void gssntlm_int_release_name(struct gssntlm_name *name);
void gssntlm_int_release_cred(struct gssntlm_cred *cred);
Expand Down
46 changes: 31 additions & 15 deletions src/gss_sec_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
{
struct gssntlm_ctx *ctx;
struct gssntlm_name *server = NULL;
struct gssntlm_name *anonymous = NULL;
struct gssntlm_cred *cred = NULL;
char *computer_name = NULL;
char *nb_computer_name = NULL;
Expand Down Expand Up @@ -79,16 +80,16 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,

if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
if (req_flags & GSS_C_ANON_FLAG) {
set_GSSERRS(ERR_NOARG, GSS_S_UNAVAILABLE);
goto done;
} else {
retmaj = gssntlm_acquire_cred(&retmin,
NULL, time_req,
NULL, GSS_C_INITIATE,
(gss_cred_id_t *)&cred,
NULL, time_rec);
retmaj = gssntlm_import_name(&retmin, NULL, GSS_C_NT_ANONYMOUS,
(gss_name_t *)&anonymous);
if (retmaj) goto done;
}
retmaj = gssntlm_acquire_cred(&retmin,
(gss_name_t)anonymous, time_req,
NULL, GSS_C_INITIATE,
(gss_cred_id_t *)&cred,
NULL, time_rec);
if (retmaj) goto done;
} else {
cred = (struct gssntlm_cred *)claimant_cred_handle;
if (cred->type != GSSNTLM_CRED_USER &&
Expand All @@ -107,8 +108,13 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
goto done;
}

retmin = gssntlm_copy_name(&cred->cred.user.user,
&ctx->source_name);
if (req_flags & GSS_C_ANON_FLAG) {
retmin = gssntlm_copy_name(anonymous,
&ctx->source_name);
} else {
retmin = gssntlm_copy_name(&cred->cred.user.user,
&ctx->source_name);
}
if (retmin) {
set_GSSERR(retmin);
goto done;
Expand Down Expand Up @@ -388,7 +394,7 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
* negotiated flags */
ctx->neg_flags &= in_flags;

retmaj = gssntlm_cli_auth(&retmin, ctx, cred, &target_info,
retmaj = gssntlm_cli_auth(&retmin, ctx, cred, &target_info,
in_flags, input_chan_bindings);
if (retmaj) goto done;

Expand Down Expand Up @@ -441,6 +447,7 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
gssntlm_release_cred(&tmpmin, (gss_cred_id_t *)&cred);
}
gssntlm_release_name(&tmpmin, (gss_name_t *)&client_name);
gssntlm_release_name(&tmpmin, (gss_name_t *)&anonymous);
safefree(computer_name);
safefree(nb_computer_name);
safefree(nb_domain_name);
Expand Down Expand Up @@ -651,7 +658,6 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
}

ctx->neg_flags = NTLMSSP_DEFAULT_SERVER_FLAGS;
/* Fixme: How do we allow anonymous negotition ? */

if (gssntlm_sec_lm_ok(ctx)) {
ctx->neg_flags |= NTLMSSP_REQUEST_NON_NT_SESSION_KEY;
Expand Down Expand Up @@ -847,9 +853,17 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
(((lm_chal_resp.length == 1) && (lm_chal_resp.data[0] == '\0')) ||
(lm_chal_resp.length == 0))) {
/* Anonymous auth */
/* FIXME: not supported for now */
set_GSSERR(ERR_NOTSUPPORTED);
goto done;
if (!gssntlm_is_anonymous_allowed()) {
set_GSSERRS(ERR_NOUSRCRED, GSS_S_DEFECTIVE_CREDENTIAL);
goto done;
}

retmaj = gssntlm_import_name(&retmin, NULL, GSS_C_NT_ANONYMOUS,
(gss_name_t *)&ctx->source_name);
if (retmaj) goto done;

/* nullSession */
memset(key_exchange_key.data, 0, 16);

} else {

Expand Down Expand Up @@ -1008,6 +1022,8 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
if (time_rec) *time_rec = GSS_C_INDEFINITE;
}
*context_handle = (gss_ctx_id_t)ctx;
gssntlm_release_cred(&tmpmin, (gss_cred_id_t *)&usr_cred);
gssntlm_release_name(&tmpmin, (gss_name_t *)&gss_usrname);
gssntlm_release_name(&tmpmin, (gss_name_t *)&server_name);
safefree(computer_name);
safefree(nb_computer_name);
Expand Down
Loading