/* * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "ntlm.h" #include #include /* * */ struct ntlmdgst { heim_ipc ipc; char *domain; OM_uint32 flags; struct ntlm_buf key; krb5_data sessionkey; }; static OM_uint32 dstg_destroy(OM_uint32 *, void *); /* * */ static OM_uint32 dstg_alloc(OM_uint32 *minor, void **ctx) { krb5_error_code ret; struct ntlmdgst *c; c = calloc(1, sizeof(*c)); if (c == NULL) { *minor = ENOMEM; return GSS_S_FAILURE; } ret = heim_ipc_init_context("ANY:org.h5l.ntlm-service", &c->ipc); if (ret) { free(c); *minor = ENOMEM; return GSS_S_FAILURE; } *ctx = c; return GSS_S_COMPLETE; } static int dstg_probe(OM_uint32 *minor_status, void *ctx, const char *realm, unsigned int *flags) { struct ntlmdgst *c = ctx; heim_idata dreq, drep; NTLMInitReply ir; size_t size; NTLMInit ni; int ret; memset(&ni, 0, sizeof(ni)); ni.flags = 0; ASN1_MALLOC_ENCODE(NTLMInit, dreq.data, dreq.length, &ni, &size, ret); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } if (size != dreq.length) abort(); ret = heim_ipc_call(c->ipc, &dreq, &drep, NULL); free(dreq.data); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = decode_NTLMInitReply(drep.data, drep.length, &ir, &size); free(drep.data); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } if (ir.ntlmNegFlags & NTLM_NEG_SIGN) *flags |= NSI_NO_SIGNING; free_NTLMInitReply(&ir); return 0; } /* * */ static OM_uint32 dstg_destroy(OM_uint32 *minor, void *ctx) { struct ntlmdgst *c = ctx; krb5_data_free(&c->sessionkey); if (c->ipc) heim_ipc_free_context(c->ipc); memset(c, 0, sizeof(*c)); free(c); return GSS_S_COMPLETE; } /* * */ static OM_uint32 dstg_ti(OM_uint32 *minor_status, ntlm_ctx ntlmctx, void *ctx, const char *hostname, const char *domain, uint32_t *negNtlmFlags) { struct ntlmdgst *c = ctx; OM_uint32 maj_stat = GSS_S_FAILURE; heim_idata dreq, drep; NTLMInitReply ir; size_t size; NTLMInit ni; int ret; memset(&ni, 0, sizeof(ni)); memset(&ir, 0, sizeof(ir)); ni.flags = 0; if (hostname) ni.hostname = (char **)&hostname; if (domain) ni.domain = (char **)&domain; ASN1_MALLOC_ENCODE(NTLMInit, dreq.data, dreq.length, &ni, &size, ret); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } if (size != dreq.length) abort(); ret = heim_ipc_call(c->ipc, &dreq, &drep, NULL); free(dreq.data); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = decode_NTLMInitReply(drep.data, drep.length, &ir, &size); free(drep.data); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } { struct ntlm_buf buf; buf.data = ir.targetinfo.data; buf.length = ir.targetinfo.length; ret = heim_ntlm_decode_targetinfo(&buf, 1, &ntlmctx->ti); if (ret) { free_NTLMInitReply(&ir); *minor_status = ret; return GSS_S_FAILURE; } } *negNtlmFlags = ir.ntlmNegFlags; maj_stat = GSS_S_COMPLETE; free_NTLMInitReply(&ir); return maj_stat; } /* * */ static OM_uint32 dstg_type3(OM_uint32 *minor_status, ntlm_ctx ntlmctx, void *ctx, const struct ntlm_type3 *type3, ntlm_cred acceptor_cred, uint32_t *flags, uint32_t *avflags, struct ntlm_buf *sessionkey, ntlm_name *name, struct ntlm_buf *uuid, struct ntlm_buf *pac) { struct ntlmdgst *c = ctx; krb5_error_code ret; NTLMRequest2 req; NTLMReply rep; heim_idata dreq, drep; size_t size; *avflags = *flags = 0; sessionkey->data = NULL; sessionkey->length = 0; *name = NULL; uuid->data = NULL; uuid->length = 0; pac->data = NULL; pac->length = 0; memset(&req, 0, sizeof(req)); memset(&rep, 0, sizeof(rep)); req.loginUserName = type3->username; req.loginDomainName = type3->targetname; req.workstation = type3->ws; req.ntlmFlags = type3->flags; req.lmchallenge.data = ntlmctx->challenge; req.lmchallenge.length = sizeof(ntlmctx->challenge); req.ntChallengeResponse.data = type3->ntlm.data; req.ntChallengeResponse.length = type3->ntlm.length; req.lmChallengeResponse.data = type3->lm.data; req.lmChallengeResponse.length = type3->lm.length; req.encryptedSessionKey.data = type3->sessionkey.data; req.encryptedSessionKey.length = type3->sessionkey.length; req.t2targetname = ntlmctx->ti.domainname; if (acceptor_cred) { req.acceptorUser = acceptor_cred->user; req.acceptorDomain = acceptor_cred->domain; } else { req.acceptorUser = ""; req.acceptorDomain = ""; } /* take care of type3->targetname ? */ ASN1_MALLOC_ENCODE(NTLMRequest2, dreq.data, dreq.length, &req, &size, ret); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } if (size != dreq.length) abort(); ret = heim_ipc_call(c->ipc, &dreq, &drep, NULL); free(dreq.data); if (ret) { *minor_status = ret; return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret, "ipc to digest-service failed"); } ret = decode_NTLMReply(drep.data, drep.length, &rep, &size); free(drep.data); if (ret) { *minor_status = ret; return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret, "message from digest-service malformed"); } if (rep.success != TRUE) { ret = HNTLM_ERR_AUTH; gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret, "ntlm: authentication failed"); goto out; } *flags = rep.ntlmFlags; *avflags = rep.avflags; if (rep.avflags & NTLM_TI_AV_FLAG_GUEST) *flags |= NTLM_NEG_ANONYMOUS; /* handle session key */ if (rep.sessionkey) { sessionkey->data = malloc(rep.sessionkey->length); memcpy(sessionkey->data, rep.sessionkey->data, rep.sessionkey->length); sessionkey->length = rep.sessionkey->length; } *name = calloc(1, sizeof(**name)); if (*name == NULL) goto out; (*name)->user = strdup(rep.user); (*name)->domain = strdup(rep.domain); if ((*name)->user == NULL || (*name)->domain == NULL) goto out; if (rep.uuid) { uuid->data = malloc(rep.uuid->length); memcpy(uuid->data, rep.uuid->data, rep.uuid->length); uuid->length = rep.uuid->length; } free_NTLMReply(&rep); return 0; out: free_NTLMReply(&rep); *minor_status = ret; return GSS_S_FAILURE; } /* * */ static void dstg_free_buffer(struct ntlm_buf *sessionkey) { if (sessionkey->data) free(sessionkey->data); sessionkey->data = NULL; sessionkey->length = 0; } /* * */ struct ntlm_server_interface ntlmsspi_dstg_digest = { "digest", dstg_alloc, dstg_destroy, dstg_probe, dstg_type3, dstg_free_buffer, dstg_ti };