From dc2cf8903ca08152464ef50ec989e97640cf1248 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Sun, 23 Jun 2013 12:02:47 -0400 Subject: Initial GSS Mechanism code. Implements init sec context and basic mechanism initialization. --- Makefile.am | 3 + src/gss_ntlmssp.c | 137 ++++++++++++++- src/gss_ntlmssp.h | 146 +++++++++++++++- src/gss_sec_ctx.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/gss_spi.c | 58 ++++++ 5 files changed, 856 insertions(+), 2 deletions(-) create mode 100644 src/gss_sec_ctx.c create mode 100644 src/gss_spi.c diff --git a/Makefile.am b/Makefile.am index 798841b..b66c61a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -72,6 +72,8 @@ GN_MECHGLUE_OBJ = \ src/crypto.c \ src/ntlm_crypto.c \ src/ntlm.c \ + src/gss_spi.c \ + src/gss_sec_ctx.c \ src/gss_ntlmssp.c dist_noinst_HEADERS = \ @@ -90,6 +92,7 @@ gssntlmssp_la_CFLAGS = \ $(AM_FLAGS) gssntlmssp_la_LDFLAGS = \ $(GN_MECHGLUE_LIBS) \ + -export-symbols-regex '^gss_' \ -avoid-version \ -module diff --git a/src/gss_ntlmssp.c b/src/gss_ntlmssp.c index 7762b23..d9ebe80 100644 --- a/src/gss_ntlmssp.c +++ b/src/gss_ntlmssp.c @@ -15,11 +15,146 @@ License along with this library; if not, see . */ +#include +#include +#include + +#include +#include + #include "gss_ntlmssp.h" /* 1.3.6.1.4.1.311.2.2.10 */ -const gss_OID_desc gss_ntlm_oid = { +const gss_OID_desc gssntlm_oid = { .length = 10, .elements = "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" }; +uint8_t gssntlm_required_security(int security_level, + enum gssntlm_role role) +{ + uint8_t resp; + + /* DC defaults */ + resp = SEC_DC_LM_OK | SEC_DC_NTLM_OK | SEC_DC_V2_OK; + + switch (security_level) { + case 0: + resp |= SEC_LM_OK | SEC_NTLM_OK; + break; + case 1: + resp |= SEC_LM_OK | SEC_NTLM_OK | SEC_EXT_SEC_OK; + break; + case 2: + resp |= SEC_NTLM_OK | SEC_EXT_SEC_OK; + break; + case 3: + resp |= SEC_V2_ONLY | SEC_EXT_SEC_OK; + break; + case 4: + resp |= SEC_NTLM_OK | SEC_EXT_SEC_OK; + if (role == GSSNTLM_DOMAIN_CONTROLLER) resp &= ~SEC_DC_LM_OK; + break; + case 5: + if (role == GSSNTLM_DOMAIN_CONTROLLER) resp = SEC_DC_V2_OK; + resp |= SEC_V2_ONLY | SEC_EXT_SEC_OK; + break; + default: + resp = 0xff; + break; + } + + return resp; +} + +int gssntlm_copy_creds(struct gssntlm_cred *in, struct gssntlm_cred *out) +{ + char *dom = NULL, *usr = NULL; + int ret = 0; + + out->type = GSSNTLM_CRED_NONE; + + switch (in->type) { + case GSSNTLM_CRED_NONE: + break; + case GSSNTLM_CRED_ANON: + out->cred.anon.dummy = 1; + break; + case GSSNTLM_CRED_USER: + dom = strdup(in->cred.user.user.data.user.domain); + if (!dom) { + ret = ENOMEM; + goto done; + } + usr = strdup(in->cred.user.user.data.user.name); + if (!usr) { + ret = ENOMEM; + goto done; + } + out->cred.user.user.data.user.domain = dom; + out->cred.user.user.data.user.name = usr; + break; + case GSSNTLM_CRED_SERVER: + out->cred.server.dummy = 1; + break; + } + + out->type = in->type; + +done: + if (ret) { + safefree(dom); + safefree(usr); + } + return ret; +} + +void gssntlm_int_release_cred(struct gssntlm_cred *cred) +{ + switch (cred->type) { + case GSSNTLM_CRED_NONE: + break; + case GSSNTLM_CRED_ANON: + cred->cred.anon.dummy = 0; + break; + case GSSNTLM_CRED_USER: + safefree(cred->cred.user.user.data.user.domain); + safefree(cred->cred.user.user.data.user.name); + safezero(cred->cred.user.nt_hash.data, 16); + cred->cred.user.nt_hash.length = 0; + safezero(cred->cred.user.lm_hash.data, 16); + cred->cred.user.lm_hash.length = 0; + break; + case GSSNTLM_CRED_SERVER: + cred->cred.server.dummy = 0; + break; + } +} + +uint32_t gssntlm_acquire_cred(uint32_t *minor_status, + gss_name_t desired_name, + uint32_t time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + uint32_t *time_rec) +{ + /* FIXME: Fecth creds from somewhere */ + *minor_status = 0; + return GSS_S_CRED_UNAVAIL; +} + +uint32_t gssntlm_release_cred(uint32_t *minor_status, + gss_cred_id_t *cred_handle) +{ + *minor_status = 0; + + if (!cred_handle) return GSS_S_COMPLETE; + + gssntlm_int_release_cred((struct gssntlm_cred *)*cred_handle); + safefree(*cred_handle); + + return GSS_S_COMPLETE; +} + diff --git a/src/gss_ntlmssp.h b/src/gss_ntlmssp.h index f7164a9..882de13 100644 --- a/src/gss_ntlmssp.h +++ b/src/gss_ntlmssp.h @@ -18,6 +18,150 @@ #ifndef _GSS_NTLMSSP_H_ #define _GSS_NTLMSSP_H_ -#include +#include "ntlm.h" +#include "crypto.h" + +#define SEC_LEVEL_MIN 0 +#define SEC_LEVEL_MAX 5 + +#define SEC_LM_OK 0x01 +#define SEC_NTLM_OK 0x02 +#define SEC_EXT_SEC_OK 0x04 +#define SEC_V2_ONLY 0x08 +#define SEC_DC_LM_OK 0x10 +#define SEC_DC_NTLM_OK 0x20 +#define SEC_DC_V2_OK 0x40 + +#define NTLMSSP_DEFAULT_CLIENT_FLAGS ( \ + NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \ + NTLMSSP_NEGOTIATE_128 | \ + NTLMSSP_NEGOTIATE_56 | \ + NTLMSSP_NEGOTIATE_NTLM | \ + NTLMSSP_NEGOTIATE_UNICODE) + +struct gssntlm_name { + enum ntlm_name_type { + GSSNTLM_NAME_NONE, + GSSNTLM_NAME_USER, + GSSNTLM_NAME_SERVER + } type; + + union { + struct { + char *domain; + char *name; + } user; + struct { + char *name; + } server; + } data; +}; + +struct gssntlm_cred { + enum ntlm_cred_type { + GSSNTLM_CRED_NONE, + GSSNTLM_CRED_ANON, + GSSNTLM_CRED_USER, + GSSNTLM_CRED_SERVER + } type; + + union { + struct { + int dummy; + } anon; + struct { + struct gssntlm_name user; + struct ntlm_key nt_hash; + struct ntlm_key lm_hash; + } user; + struct { + int dummy; + } server; + } cred; +}; + +struct gssntlm_signseal { + struct ntlm_key sign_key; + struct ntlm_key seal_key; + struct ntlm_rc4_handle *seal_handle; + uint32_t seq_num; +}; + +struct gssntlm_ctx { + enum gssntlm_role { + GSSNTLM_CLIENT, + GSSNTLM_SERVER, + GSSNTLM_DOMAIN_SERVER, + GSSNTLM_DOMAIN_CONTROLLER + } role; + + enum { + NTLMSSP_STAGE_INIT = 0, + NTLMSSP_STAGE_NEGOTIATE, + NTLMSSP_STAGE_CHALLENGE, + NTLMSSP_STAGE_AUTHENTICATE, + NTLMSSP_STAGE_DONE + } stage; + + struct gssntlm_cred cred; + char *workstation; + int lm_compatibility_level; + + struct ntlm_ctx *ntlm; + struct ntlm_buffer nego_msg; + struct ntlm_buffer chal_msg; + struct ntlm_buffer auth_msg; + + /* requested gss fags */ + uint32_t gss_flags; + + /* negotiated flags */ + uint32_t neg_flags; + + /* TODO: Add whitelist of servers we are allowed to communicate with */ + + struct ntlm_key exported_session_key; + struct gssntlm_signseal send; + struct gssntlm_signseal recv; +}; + +uint8_t gssntlm_required_security(int security_level, + enum gssntlm_role role); +void gssntlm_int_release_cred(struct gssntlm_cred *cred); + +int gssntlm_copy_creds(struct gssntlm_cred *in, struct gssntlm_cred *out); + +extern const gss_OID_desc gssntlm_oid; + +uint32_t gssntlm_acquire_cred(uint32_t *minor_status, + gss_name_t desired_name, + uint32_t time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + uint32_t *time_rec); + +uint32_t gssntlm_release_cred(uint32_t *minor_status, + gss_cred_id_t *cred_handle); + +uint32_t gssntlm_init_sec_context(uint32_t *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + uint32_t req_flags, + uint32_t time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + uint32_t *ret_flags, + uint32_t *time_rec); + +uint32_t gssntlm_delete_sec_context(uint32_t *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token); + #endif /* _GSS_NTLMSSP_H_ */ diff --git a/src/gss_sec_ctx.c b/src/gss_sec_ctx.c new file mode 100644 index 0000000..34ee0a6 --- /dev/null +++ b/src/gss_sec_ctx.c @@ -0,0 +1,514 @@ +/* + Copyright (C) 2013 Simo Sorce + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include + +#include +#include + +#include "gss_ntlmssp.h" + + +uint32_t gssntlm_init_sec_context(uint32_t *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + uint32_t req_flags, + uint32_t time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + uint32_t *ret_flags, + uint32_t *time_rec) +{ + struct gssntlm_ctx *ctx; + struct gssntlm_name *server = NULL; + struct gssntlm_cred *cred = NULL; + const char *workstation = NULL; + const char *domain = NULL; + uint32_t in_flags; + uint32_t msg_type; + char *trgt_name; + uint8_t server_chal[8]; + struct ntlm_buffer challenge = { server_chal, 8 }; + struct ntlm_buffer target_info = { 0 }; + char *trginfo_name = NULL; + uint64_t srv_time = 0; + struct ntlm_buffer nt_chal_resp = { 0 }; + struct ntlm_buffer lm_chal_resp = { 0 }; + struct ntlm_buffer enc_sess_key = { 0 }; + struct ntlm_key encrypted_random_session_key = { .length = 16 }; + struct ntlm_key key_exchange_key = { .length = 16 }; + uint32_t tmpmin; + uint32_t retmin = 0; + uint32_t retmaj = 0; + uint8_t sec_req; + bool key_exch; + + /* reset return values */ + *minor_status = 0; + if (actual_mech_type) *actual_mech_type = NULL; + if (ret_flags) *ret_flags = 0; + if (time_rec) *time_rec = 0; + + if (target_name) { + server = (struct gssntlm_name *)target_name; + if (server->type != GSSNTLM_NAME_SERVER) { + return GSS_S_BAD_NAMETYPE; + } + if (!server->data.server.name || + !server->data.server.name[0]) { + return GSS_S_BAD_NAME; + } + } + + if (*context_handle == GSS_C_NO_CONTEXT) { + + if (input_token && input_token->length != 0) { + return GSS_S_DEFECTIVE_TOKEN; + } + + /* first call */ + ctx = calloc(1, sizeof(struct gssntlm_ctx)); + if (!ctx) { + retmin = ENOMEM; + retmaj = GSS_S_FAILURE; + goto done; + } + + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { + if (req_flags & GSS_C_ANON_FLAG) { + ctx->cred.type = GSSNTLM_CRED_ANON; + ctx->cred.cred.anon.dummy = 1; + } else { + retmaj = gssntlm_acquire_cred(&retmin, + NULL, 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; + } + + retmin = gssntlm_copy_creds(cred, &ctx->cred); + if (retmin != 0) { + retmaj = GSS_S_FAILURE; + goto done; + } + + ctx->gss_flags = req_flags; + + ctx->role = GSSNTLM_CLIENT; + + /* use most secure defaults for now, we can add options to relax + * security later */ + ctx->lm_compatibility_level = SEC_LEVEL_MAX; + ctx->neg_flags = NTLMSSP_DEFAULT_CLIENT_FLAGS; + + /* + * we ignore unsupported flags for now + * + * GSS_C_DELEG_FLAG + * GSS_C_MUTUAL_FLAG + * GSS_C_PROT_READY_FLAG + * GSS_C_TRANS_FLAG + * GSS_C_DELEG_POLICY_FLAG + * GSS_C_DCE_STYLE + * GSS_C_EXTENDED_ERROR_FLAG + */ + if ((req_flags & GSS_C_INTEG_FLAG) || + (req_flags & GSS_C_REPLAY_FLAG) || + (req_flags & GSS_C_SEQUENCE_FLAG)) { + ctx->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + } + if (req_flags & GSS_C_CONF_FLAG) { + ctx->neg_flags |= NTLMSSP_NEGOTIATE_SEAL | + NTLMSSP_NEGOTIATE_KEY_EXCH | + NTLMSSP_NEGOTIATE_LM_KEY | + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY; + } + if (req_flags & GSS_C_ANON_FLAG) { + ctx->neg_flags |= NTLMSSP_ANONYMOUS; + } + if (req_flags & GSS_C_IDENTIFY_FLAG) { + ctx->neg_flags |= NTLMSSP_NEGOTIATE_IDENTIFY; + } + + if (ctx->cred.type == GSSNTLM_CRED_USER && + ctx->cred.cred.user.user.data.user.domain) { + ctx->neg_flags |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED; + domain = ctx->cred.cred.user.user.data.user.domain; + } + if (ctx->workstation) { + ctx->neg_flags |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED; + workstation = ctx->workstation; + } + + sec_req = gssntlm_required_security(ctx->lm_compatibility_level, + ctx->role); + if (sec_req == 0xff) { + retmaj = GSS_S_FAILURE; + goto done; + } + if (!(sec_req & SEC_LM_OK)) { + ctx->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + if (!(sec_req & SEC_EXT_SEC_OK)) { + ctx->neg_flags &= ~NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY; + } + + retmin = ntlm_init_ctx(&ctx->ntlm); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + + retmin = ntlm_encode_neg_msg(ctx->ntlm, ctx->neg_flags, + domain, workstation, &ctx->nego_msg); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + + ctx->stage = NTLMSSP_STAGE_NEGOTIATE; + + output_token->value = malloc(ctx->nego_msg.length); + if (!output_token->value) { + retmin = ENOMEM; + retmaj = GSS_S_FAILURE; + goto done; + } + memcpy(output_token->value, ctx->nego_msg.data, ctx->nego_msg.length); + output_token->length = ctx->nego_msg.length; + + retmaj = GSS_S_CONTINUE_NEEDED; + + } else { + ctx = (struct gssntlm_ctx *)(*context_handle); + + if (ctx->role != GSSNTLM_CLIENT) { + retmaj = GSS_S_NO_CONTEXT; + goto done; + } + + ctx->chal_msg.data = malloc(input_token->length); + if (!ctx->chal_msg.data) { + retmin = ENOMEM; + retmaj = GSS_S_FAILURE; + goto done; + } + memcpy(ctx->chal_msg.data, input_token->value, input_token->length); + ctx->chal_msg.length = input_token->length; + + retmin = ntlm_decode_msg_type(ctx->ntlm, &ctx->chal_msg, &msg_type); + if (retmin) { + retmaj = GSS_S_DEFECTIVE_TOKEN; + goto done; + } + + if (msg_type != NTLMSSP_STAGE_CHALLENGE || + ctx->stage != NTLMSSP_STAGE_NEGOTIATE) { + retmaj = GSS_S_NO_CONTEXT; + goto done; + } + + retmin = ntlm_decode_chal_msg(ctx->ntlm, &ctx->chal_msg, &in_flags, + &trgt_name, &challenge, &target_info); + if (retmin) { + retmaj = GSS_S_DEFECTIVE_TOKEN; + goto done; + } + + sec_req = gssntlm_required_security(ctx->lm_compatibility_level, + ctx->role); + if (sec_req == 0xff) { + retmaj = GSS_S_FAILURE; + goto done; + } + + /* mask unacceptable flags */ + if (!(sec_req & SEC_LM_OK)) { + in_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_56)) { + in_flags &= ~NTLMSSP_NEGOTIATE_56; + } + if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_128)) { + in_flags &= ~NTLMSSP_NEGOTIATE_128; + } + if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { + in_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH; + } + if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_OEM)) { + in_flags &= ~NTLMSSP_NEGOTIATE_OEM; + } + if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_UNICODE)) { + in_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; + } + + /* check required flags */ + if ((ctx->neg_flags & NTLMSSP_NEGOTIATE_SEAL) && + (!(in_flags & NTLMSSP_NEGOTIATE_SEAL))) { + retmaj = GSS_S_FAILURE; + goto done; + } + if ((ctx->neg_flags & NTLMSSP_NEGOTIATE_SIGN) && + (!(in_flags & NTLMSSP_NEGOTIATE_SIGN))) { + retmaj = GSS_S_FAILURE; + goto done; + } + + if (!(in_flags & (NTLMSSP_NEGOTIATE_OEM | + NTLMSSP_NEGOTIATE_UNICODE))) { + /* no common understanding */ + retmaj = GSS_S_FAILURE; + goto done; + } + + 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) { + retmin = ENOMEM; + retmaj = GSS_S_FAILURE; + goto done; + } + lm_chal_resp.data[0] = 0; + lm_chal_resp.length = 1; + + } else if (sec_req & SEC_V2_ONLY) { + + /* ### NTLMv2 ### */ + uint8_t client_chal[8]; + struct ntlm_buffer cli_chal = { client_chal, 8 }; + struct ntlm_key ntlmv2_key = { .length = 16 }; + struct ntlm_buffer nt_proof = { 0 }; + + if (target_info.length == 0) { + retmaj = GSS_S_DEFECTIVE_TOKEN; + goto done; + } + + /* TODO: check that returned netbios/dns names match ? */ + /* TODO: support SingleHost and ChannelBindings buffers */ + /* NOTE: target_info should be re-encoded in the client to + * augment it with correct client av_flags, but we skip that + * for now, this means we never set the MIC flag either */ + retmin = ntlm_decode_target_info(ctx->ntlm, &target_info, + NULL, NULL, NULL, NULL, NULL, + &trginfo_name, NULL, + &srv_time, NULL, NULL); + if (retmin) { + if (retmin == ERR_DECODE) { + retmaj = GSS_S_DEFECTIVE_TOKEN; + } else { + retmaj = GSS_S_FAILURE; + } + goto done; + } + + if (server && trginfo_name) { + if (strcasecmp(server->data.server.name, trginfo_name) != 0) { + retmin = EINVAL; + retmaj = GSS_S_FAILURE; + goto done; + } + } + + /* the server did not send the timestamp, use current time */ + if (srv_time == 0) { + srv_time = ntlm_timestamp_now(); + } + + /* Random client challenge */ + retmin = RAND_BUFFER(&cli_chal); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + + /* NTLMv2 Key */ + retmin = NTOWFv2(ctx->ntlm, &ctx->cred.cred.user.nt_hash, + ctx->cred.cred.user.user.data.user.name, + ctx->cred.cred.user.user.data.user.domain, + &ntlmv2_key); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + + /* NTLMv2 Response */ + retmin = ntlmv2_compute_nt_response(&ntlmv2_key, + server_chal, client_chal, + srv_time, &target_info, + &nt_chal_resp); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + + /* LMv2 Response */ + retmin = ntlmv2_compute_lm_response(&ntlmv2_key, + server_chal, client_chal, + &lm_chal_resp); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + + /* The NT proof is the first 16 bytes */ + nt_proof.data = nt_chal_resp.data; + nt_proof.length = 16; + + /* The Session Base Key */ + /* In NTLMv2 the Key Exchange Key is the Session Base Key */ + retmin = ntlmv2_session_base_key(&ntlmv2_key, &nt_proof, + &key_exchange_key); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + } else { + /* ### NTLMv1 ### */ + + } + + key_exch = (in_flags & NTLMSSP_NEGOTIATE_KEY_EXCH); + + retmin = ntlm_exported_session_key(&key_exchange_key, key_exch, + &ctx->exported_session_key); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + + if (key_exch) { + retmin = ntlm_encrypted_session_key(&key_exchange_key, + &ctx->exported_session_key, + &encrypted_random_session_key); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + } + + if (in_flags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL)) { + retmin = ntlm_signseal_keys(in_flags, + (ctx->role == GSSNTLM_CLIENT), + &ctx->exported_session_key, + &ctx->send.sign_key, + &ctx->recv.sign_key, + &ctx->send.seal_key, + &ctx->recv.seal_key, + &ctx->send.seal_handle, + &ctx->recv.seal_handle); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + } + + /* TODO: Compute MIC if necessary */ + + /* in_flags all verified, assign as current flags */ + ctx->neg_flags |= in_flags; + enc_sess_key.data = encrypted_random_session_key.data; + enc_sess_key.length = encrypted_random_session_key.length; + + retmin = ntlm_encode_auth_msg(ctx->ntlm, ctx->neg_flags, + &lm_chal_resp, &nt_chal_resp, + ctx->cred.cred.user.user.data.user.domain, + ctx->cred.cred.user.user.data.user.name, + ctx->workstation, &enc_sess_key, NULL, + &ctx->auth_msg); + if (retmin) { + retmaj = GSS_S_FAILURE; + goto done; + } + + ctx->stage = NTLMSSP_STAGE_AUTHENTICATE; + + output_token->value = malloc(ctx->auth_msg.length); + if (!output_token->value) { + retmin = ENOMEM; + retmaj = GSS_S_FAILURE; + goto done; + } + memcpy(output_token->value, ctx->auth_msg.data, ctx->auth_msg.length); + output_token->length = ctx->auth_msg.length; + + retmaj = GSS_S_COMPLETE; + } + +done: + if ((retmaj != GSS_S_COMPLETE) && + (retmaj != GSS_S_CONTINUE_NEEDED)) { + gssntlm_delete_sec_context(&tmpmin, (gss_ctx_id_t *)&ctx, NULL); + *minor_status = retmin; + } + *context_handle = (gss_ctx_id_t)ctx; + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { + /* we copy creds around, so always free if not passed in */ + gssntlm_release_cred(&tmpmin, (gss_cred_id_t *)&cred); + } + ntlm_free_buffer_data(&target_info); + ntlm_free_buffer_data(&nt_chal_resp); + ntlm_free_buffer_data(&lm_chal_resp); + return retmaj; +} + +uint32_t gssntlm_delete_sec_context(uint32_t *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + struct gssntlm_ctx *ctx; + int ret; + + *minor_status = 0; + + if (!context_handle) return GSS_S_CALL_INACCESSIBLE_READ; + if (*context_handle == NULL) return GSS_S_COMPLETE; + + ctx = (struct gssntlm_ctx *)*context_handle; + + gssntlm_int_release_cred(&ctx->cred); + ctx->cred.type = GSSNTLM_CRED_NONE; + + ret = ntlm_free_ctx(&ctx->ntlm); + + safefree(ctx->nego_msg.data); + safefree(ctx->chal_msg.data); + safefree(ctx->auth_msg.data); + ctx->nego_msg.length = 0; + ctx->chal_msg.length = 0; + ctx->auth_msg.length = 0; + + safefree(*context_handle); + + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + return GSS_S_COMPLETE; +} diff --git a/src/gss_spi.c b/src/gss_spi.c new file mode 100644 index 0000000..46fd176 --- /dev/null +++ b/src/gss_spi.c @@ -0,0 +1,58 @@ +/* + Copyright (C) 2013 Simo Sorce + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include + +#include "gss_ntlmssp.h" + +OM_uint32 gss_init_sec_context(OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + return gssntlm_init_sec_context(minor_status, + claimant_cred_handle, + context_handle, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec); +} + +OM_uint32 gss_delete_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + return gssntlm_delete_sec_context(minor_status, + context_handle, + output_token); +} -- cgit