1/*
2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "ntlm.h"
37#include <heim-ipc.h>
38#include <digest_asn1.h>
39
40/*
41 *
42 */
43
44struct ntlmdgst {
45    heim_ipc ipc;
46    char *domain;
47    OM_uint32 flags;
48    struct ntlm_buf key;
49    krb5_data sessionkey;
50};
51
52static OM_uint32 dstg_destroy(OM_uint32 *, void *);
53
54/*
55 *
56 */
57
58static OM_uint32
59dstg_alloc(OM_uint32 *minor, void **ctx)
60{
61    krb5_error_code ret;
62    struct ntlmdgst *c;
63
64    c = calloc(1, sizeof(*c));
65    if (c == NULL) {
66	*minor = ENOMEM;
67	return GSS_S_FAILURE;
68    }
69
70    ret = heim_ipc_init_context("ANY:org.h5l.ntlm-service", &c->ipc);
71    if (ret) {
72	free(c);
73	*minor = ENOMEM;
74	return GSS_S_FAILURE;
75    }
76
77    *ctx = c;
78
79    return GSS_S_COMPLETE;
80}
81
82static int
83dstg_probe(OM_uint32 *minor_status, void *ctx, const char *realm, unsigned int *flags)
84{
85    struct ntlmdgst *c = ctx;
86    heim_idata dreq, drep;
87    NTLMInitReply ir;
88    size_t size = 0;
89    NTLMInit ni;
90    int ret;
91
92    memset(&ni, 0, sizeof(ni));
93
94    ni.flags = 0;
95
96    ASN1_MALLOC_ENCODE(NTLMInit, dreq.data, dreq.length, &ni, &size, ret);
97    if (ret) {
98	*minor_status = ret;
99	return GSS_S_FAILURE;
100    }
101    if (size != dreq.length)
102	abort();
103
104    ret = heim_ipc_call(c->ipc, &dreq, &drep, NULL);
105    free(dreq.data);
106    if (ret) {
107	*minor_status = ret;
108	return GSS_S_FAILURE;
109    }
110
111    ret = decode_NTLMInitReply(drep.data, drep.length, &ir, &size);
112    free(drep.data);
113    if (ret) {
114	*minor_status = ret;
115	return GSS_S_FAILURE;
116    }
117
118    if (ir.ntlmNegFlags & NTLM_NEG_SIGN)
119	*flags |= NSI_NO_SIGNING;
120
121    free_NTLMInitReply(&ir);
122
123    return 0;
124}
125
126/*
127 *
128 */
129
130static OM_uint32
131dstg_destroy(OM_uint32 *minor, void *ctx)
132{
133    struct ntlmdgst *c = ctx;
134    krb5_data_free(&c->sessionkey);
135    if (c->ipc)
136	heim_ipc_free_context(c->ipc);
137    memset(c, 0, sizeof(*c));
138    free(c);
139
140    return GSS_S_COMPLETE;
141}
142
143/*
144 *
145 */
146
147static OM_uint32
148dstg_ti(OM_uint32 *minor_status,
149	ntlm_ctx ntlmctx,
150	void *ctx,
151	const char *hostname,
152	const char *domain,
153	uint32_t *negNtlmFlags)
154{
155    struct ntlmdgst *c = ctx;
156    OM_uint32 maj_stat = GSS_S_FAILURE;
157    heim_idata dreq, drep;
158    NTLMInitReply ir;
159    size_t size = 0;
160    NTLMInit ni;
161    int ret;
162
163    memset(&ni, 0, sizeof(ni));
164    memset(&ir, 0, sizeof(ir));
165
166    ni.flags = 0;
167    if (hostname)
168	ni.hostname = (char **)&hostname;
169    if (domain)
170	ni.domain = (char **)&domain;
171
172    ASN1_MALLOC_ENCODE(NTLMInit, dreq.data, dreq.length, &ni, &size, ret);
173    if (ret) {
174	*minor_status = ret;
175	return GSS_S_FAILURE;
176    }
177    if (size != dreq.length)
178	abort();
179
180    ret = heim_ipc_call(c->ipc, &dreq, &drep, NULL);
181    free(dreq.data);
182    if (ret) {
183	*minor_status = ret;
184	return GSS_S_FAILURE;
185    }
186
187    ret = decode_NTLMInitReply(drep.data, drep.length, &ir, &size);
188    free(drep.data);
189    if (ret) {
190	*minor_status = ret;
191	return GSS_S_FAILURE;
192    }
193
194    {
195	struct ntlm_buf buf;
196
197	buf.data = ir.targetinfo.data;
198	buf.length = ir.targetinfo.length;
199
200	ret = heim_ntlm_decode_targetinfo(&buf, 1, &ntlmctx->ti);
201	if (ret) {
202	    free_NTLMInitReply(&ir);
203	    *minor_status = ret;
204	    return GSS_S_FAILURE;
205	}
206    }
207    *negNtlmFlags = ir.ntlmNegFlags;
208
209    maj_stat = GSS_S_COMPLETE;
210
211    free_NTLMInitReply(&ir);
212
213    return maj_stat;
214}
215
216/*
217 *
218 */
219
220static OM_uint32
221dstg_type3(OM_uint32 *minor_status,
222	   ntlm_ctx ntlmctx,
223	   void *ctx,
224	   const struct ntlm_type3 *type3,
225	   ntlm_cred acceptor_cred,
226	   uint32_t *flags,
227	   uint32_t *avflags,
228	   struct ntlm_buf *sessionkey,
229	   ntlm_name *name, struct ntlm_buf *uuid,
230	   struct ntlm_buf *pac)
231{
232    struct ntlmdgst *c = ctx;
233    krb5_error_code ret;
234    NTLMRequest2 req;
235    NTLMReply rep;
236    heim_idata dreq, drep;
237    size_t size = 0;
238
239    *avflags = *flags = 0;
240
241    sessionkey->data = NULL;
242    sessionkey->length = 0;
243    *name = NULL;
244    uuid->data = NULL;
245    uuid->length = 0;
246    pac->data = NULL;
247    pac->length = 0;
248
249    memset(&req, 0, sizeof(req));
250    memset(&rep, 0, sizeof(rep));
251
252    req.loginUserName = type3->username;
253    req.loginDomainName = type3->targetname;
254    req.workstation = type3->ws;
255    req.ntlmFlags = type3->flags;
256    req.lmchallenge.data = ntlmctx->challenge;
257    req.lmchallenge.length = sizeof(ntlmctx->challenge);
258    req.ntChallengeResponse.data = type3->ntlm.data;
259    req.ntChallengeResponse.length = type3->ntlm.length;
260    req.lmChallengeResponse.data = type3->lm.data;
261    req.lmChallengeResponse.length = type3->lm.length;
262    req.encryptedSessionKey.data = type3->sessionkey.data;
263    req.encryptedSessionKey.length = type3->sessionkey.length;
264    req.t2targetname = ntlmctx->ti.domainname;
265    if (acceptor_cred) {
266	req.acceptorUser = acceptor_cred->user;
267	req.acceptorDomain = acceptor_cred->domain;
268    } else {
269	req.acceptorUser = "";
270	req.acceptorDomain = "";
271    }
272
273    /* take care of type3->targetname ? */
274
275    ASN1_MALLOC_ENCODE(NTLMRequest2, dreq.data, dreq.length, &req, &size, ret);
276    if (ret) {
277	*minor_status = ret;
278	return GSS_S_FAILURE;
279    }
280    if (size != dreq.length)
281	abort();
282
283    ret = heim_ipc_call(c->ipc, &dreq, &drep, NULL);
284    free(dreq.data);
285    if (ret) {
286	*minor_status = ret;
287	return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret,
288				       "ipc to digest-service failed");
289    }
290
291    ret = decode_NTLMReply(drep.data, drep.length, &rep, &size);
292    free(drep.data);
293    if (ret) {
294	*minor_status = ret;
295	return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret,
296				       "message from digest-service malformed");
297    }
298
299    if (rep.success != TRUE) {
300	ret = HNTLM_ERR_AUTH;
301	gss_mg_set_error_string(GSS_NTLM_MECHANISM,
302				GSS_S_FAILURE, ret,
303				"ntlm: authentication failed");
304	goto out;
305    }
306
307    *flags = rep.ntlmFlags;
308    *avflags = rep.avflags;
309
310    if (rep.avflags & NTLM_TI_AV_FLAG_GUEST)
311	*flags |= NTLM_NEG_ANONYMOUS;
312
313    /* handle session key */
314    if (rep.sessionkey) {
315	sessionkey->data = malloc(rep.sessionkey->length);
316	memcpy(sessionkey->data, rep.sessionkey->data,
317	       rep.sessionkey->length);
318	sessionkey->length = rep.sessionkey->length;
319    }
320
321    *name = calloc(1, sizeof(**name));
322    if (*name == NULL)
323	goto out;
324    (*name)->user = strdup(rep.user);
325    (*name)->domain = strdup(rep.domain);
326    if ((*name)->user == NULL || (*name)->domain == NULL)
327	goto out;
328
329    if (rep.uuid) {
330	uuid->data = malloc(rep.uuid->length);
331	memcpy(uuid->data, rep.uuid->data, rep.uuid->length);
332	uuid->length = rep.uuid->length;
333    }
334
335    free_NTLMReply(&rep);
336
337    return 0;
338
339 out:
340    free_NTLMReply(&rep);
341    *minor_status = ret;
342    return GSS_S_FAILURE;
343}
344
345/*
346 *
347 */
348
349static void
350dstg_free_buffer(struct ntlm_buf *sessionkey)
351{
352    if (sessionkey->data)
353	free(sessionkey->data);
354    sessionkey->data = NULL;
355    sessionkey->length = 0;
356}
357
358/*
359 *
360 */
361
362struct ntlm_server_interface ntlmsspi_dstg_digest = {
363    "digest",
364    dstg_alloc,
365    dstg_destroy,
366    dstg_probe,
367    dstg_type3,
368    dstg_free_buffer,
369    dstg_ti
370};
371