summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <idra@samba.org>2013-06-23 12:02:47 -0400
committerSimo Sorce <simo@redhat.com>2013-07-18 22:37:25 -0400
commitdc2cf8903ca08152464ef50ec989e97640cf1248 (patch)
tree219cdd208441557fe6187dd8104b95d498baba8c
parent994d9639caeec0164134e3bd6c16512defe93021 (diff)
downloadgss-ntlmssp-dc2cf8903ca08152464ef50ec989e97640cf1248.tar.gz
gss-ntlmssp-dc2cf8903ca08152464ef50ec989e97640cf1248.tar.xz
gss-ntlmssp-dc2cf8903ca08152464ef50ec989e97640cf1248.zip
Initial GSS Mechanism code.
Implements init sec context and basic mechanism initialization.
-rw-r--r--Makefile.am3
-rw-r--r--src/gss_ntlmssp.c137
-rw-r--r--src/gss_ntlmssp.h146
-rw-r--r--src/gss_sec_ctx.c514
-rw-r--r--src/gss_spi.c58
5 files changed, 856 insertions, 2 deletions
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 <http://www.gnu.org/licenses/>.
*/
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
#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 <gssapi/gssapi.h>
+#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 <simo@samba.org>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
+#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 <simo@samba.org>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <gssapi/gssapi.h>
+
+#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);
+}