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 - 2011 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#define HC_DEPRECATED_CRYPTO
37
38#include "headers.h"
39
40static void usage(int ret) __attribute__((noreturn));
41
42typedef struct pk_client_params pk_client_params;
43struct DigestREQ;
44struct Kx509Request;
45typedef struct kdc_request_desc *kdc_request_t;
46
47#include <kdc-private.h>
48
49#include <asn1-common.h>
50#include <digest_asn1.h>
51#include <heimntlm.h>
52#include <roken.h>
53
54#include <gssapi.h>
55#include <gssapi_spi.h>
56#include <gssapi_ntlm.h>
57
58#include "hex.h"
59#include "heimbase.h"
60
61#ifdef HAVE_OPENDIRECTORY
62
63#include <CoreFoundation/CoreFoundation.h>
64#include <OpenDirectory/OpenDirectory.h>
65#include <SystemConfiguration/SCPreferences.h>
66
67#ifdef __APPLE_PRIVATE__
68#include <OpenDirectory/OpenDirectoryPriv.h>
69#endif
70
71
72#endif
73
74#if defined(__APPLE_PRIVATE__) && !defined(__APPLE_TARGET_EMBEDDED__)
75#include <dlfcn.h>
76#define HAVE_APPLE_NETLOGON 1
77#endif
78
79
80#ifdef __APPLE__
81#include <sandbox.h>
82
83static int sandbox_flag = 1;
84#endif
85static int use_unix_flag = 0;
86static int help_flag = 0;
87static int version_flag = 0;
88
89
90
91static krb5_kdc_configuration *config = NULL;
92
93static void
94fillin_hostinfo(struct ntlm_targetinfo *ti);
95
96static void
97digest_debug_key(krb5_context context, int level,
98		 const char *name, const void *data, size_t size)
99{
100    char *hex;
101
102    /**
103     * Only print the first 2 bytes though since we really don't want
104     * the full key sprinkled though logs.
105     */
106    size = min(size, 2);
107
108    if (hex_encode(data, size, &hex) < 0)
109	return;
110
111    kdc_log(context, config, level, "%s: %s", name, hex);
112    memset(hex, 0, strlen(hex));
113    free(hex);
114}
115
116
117
118static const char *
119derive_version_ntq(const NTLMRequest2 *ntq)
120{
121    if ((ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) &&
122	(ntq->lmChallengeResponse.length >= 8) &&
123	(ntq->ntChallengeResponse.length == 24))
124	return "ntlmv1-with-v2-session";
125    if (ntq->ntChallengeResponse.length == 24)
126	return "ntlmv1";
127    if (ntq->ntChallengeResponse.length > 24)
128	return "ntlmv2";
129    if (ntq->ntChallengeResponse.length == 0)
130	return "lm";
131    return "unknown";
132}
133
134static void
135log_complete(krb5_context context,
136	     const char *type,
137	     const NTLMReply *ntp,
138	     krb5_error_code ret,
139	     const char *ntlm_version)
140{
141
142    if (ntp->success) {
143	char str[256] = { 0 } ;
144	heim_ntlm_unparse_flags(ntp->ntlmFlags, str, sizeof(str));
145
146	kdc_log(context, config, 1, "digest-request %s: ok user=%s\\%s proto=%s flags: %s",
147		type,
148		ntp->domain, ntp->user, ntlm_version, str);
149    } else
150	kdc_log(context, config, 1, "digest-request: %s failed with %d proto=%s", type, ret, ntlm_version);
151}
152
153
154#ifdef ENABLE_NTLM
155
156static int
157validate_local(krb5_context context,
158	       const char *key,
159	       const char *local,
160	       const char *client)
161{
162    if (local == NULL)
163	return 0;
164    if (client == NULL || strcmp(local, client) != 0) {
165	kdc_log(context, config, 1, "validate failed for key %s: %s client: %s",
166		key, local, client ? client : "<not sent>");
167	return EAUTH;
168    }
169    return 0;
170}
171
172/*
173 * Validate that the client sent back the expected target info,
174 * the important keys are:
175 * - servername
176 * - timestamp
177 */
178
179static int
180validate_targetinfo(krb5_context context,
181		    struct ntlm_buf *tibuf,
182		    const char *domain,
183		    const char *acceptorUser,
184		    uint32_t *avflags)
185{
186    struct ntlm_targetinfo localti;
187    struct ntlm_targetinfo ti;
188    int ret;
189
190    if (tibuf->length == 0)
191	return EAUTH;
192
193    memset(&localti, 0, sizeof(localti));
194
195    fillin_hostinfo(&localti);
196
197    ret = heim_ntlm_decode_targetinfo(tibuf, 1, &ti);
198    if (ret)
199	goto out;
200
201    if (ti.timestamp) {
202	time_t now = time(NULL);
203	/*
204	 * "now" should be equal or forward in compared to
205	 * ti.timestamp, but lets be liberal here.
206	 */
207	if (krb5_time_abs(now, heim_ntlm_ts2unixtime(ti.timestamp)) > heim_ntlm_time_skew) {
208	    ret = EAUTH;
209	    goto out;
210	}
211    }
212
213    ret = validate_local(context, "servername", localti.servername, ti.servername);
214    if (ret)
215	goto out;
216    ret = validate_local(context, "domainname", domain, ti.domainname);
217    if (ret)
218	goto out;
219    if (domain == NULL) {
220	ret = validate_local(context, "domainname", localti.domainname, ti.domainname);
221	if (ret)
222	    goto out;
223    }
224
225    *avflags = ti.avflags;
226
227    /*
228     * Check that the service that the client send is matching what
229     * the server claims to be.
230     */
231    if (acceptorUser[0] != '\0' && ti.targetname) {
232	const char *p = strchr(ti.targetname, '/');
233	size_t len = strlen(acceptorUser);
234
235	if (p && len != (size_t)(p - ti.targetname) &&
236	    strncasecmp(ti.targetname, acceptorUser, len) != 0)
237	{
238	    ret = EAUTH;
239	    goto out;
240	}
241    }
242
243    /* more validation here
244    char *dnstreename;
245    char *targetname;
246    uint32_t avflags;
247    struct ntlm_buf channel_bindings;
248    */
249
250 out:
251    heim_ntlm_free_targetinfo(&ti);
252    heim_ntlm_free_targetinfo(&localti);
253
254    return ret;
255}
256
257
258
259struct ntlm_ftable {
260    void		*ctx;
261    krb5_error_code	(*init)(krb5_context, void **);
262    void		(*fini)(void *);
263    int			(*ti)(void *, struct ntlm_targetinfo *ti);
264    uint32_t		(*filter_flags)(krb5_context, void *);
265    int			(*authenticate)(void *ctx,
266					krb5_context context,
267					const NTLMRequest2 *ntq,
268					void *response_ctx,
269					void (response)(void *, const NTLMReply *reply));
270};
271
272static uint32_t
273kdc_flags(krb5_context context, void *ctx)
274{
275    return 0;
276}
277
278
279static int
280kdc_authenticate(void *ctx,
281		 krb5_context context,
282		 const NTLMRequest2 *ntq,
283		 void *response_ctx,
284		 void (response)(void *, const NTLMReply *ntp))
285{
286    krb5_principal client = NULL;
287    unsigned char sessionkey[16];
288    krb5_data sessionkeydata;
289    char *user_name = NULL;
290    hdb_entry_ex *user = NULL;
291    HDB *clientdb = NULL;
292    Key *key = NULL;
293    NTLMReply ntp;
294    const char *ntlm_version = "unknown";
295    int ret;
296
297    krb5_data_zero(&sessionkeydata);
298    memset(&ntp, 0, sizeof(ntp));
299
300    kdc_log(context, config, 1, "digest-request: user=%s\\%s",
301	    ntq->loginDomainName, ntq->loginUserName);
302
303    if (ntq->lmchallenge.length != 8){
304	ret = HNTLM_ERR_INVALID_CHALLANGE;
305	goto failed;
306    }
307
308    if (ntq->ntChallengeResponse.length == 0) {
309	ret = HNTLM_ERR_INVALID_NT_RESPONSE;
310	goto failed;
311    }
312
313    ret = krb5_make_principal(context, &client, ntq->loginDomainName,
314			      ntq->loginUserName, NULL);
315    if (ret)
316	goto failed;
317
318    krb5_principal_set_type(context, client, KRB5_NT_NTLM);
319
320    ret = krb5_unparse_name(context, client, &user_name);
321    if (ret)
322	goto failed;
323
324    ret = _kdc_db_fetch(context, config, client,
325			HDB_F_GET_CLIENT, NULL, &clientdb, &user);
326    if (ret)
327	goto failed;
328
329    ret = kdc_check_flags(context, config, user, user_name, NULL, NULL, 1);
330    if (ret) {
331	kdc_log(context, config, 2,
332		"digest-request: user %s\\%s, invalid",
333		ntq->loginDomainName, ntq->loginUserName);
334	goto failed;
335    }
336
337    if (user->entry.principal->name.name_string.len < 1) {
338	ret = HNTLM_ERR_NOT_CONFIGURED;
339	kdc_log(context, config, 2,
340		"digest-request: user %s\\%s, have weired name",
341		ntq->loginDomainName, ntq->loginUserName);
342	goto failed;
343    }
344
345    ntp.domain = user->entry.principal->realm;
346    ntp.user = user->entry.principal->name.name_string.val[0];
347
348    ret = hdb_enctype2key(context, &user->entry,
349			  ETYPE_ARCFOUR_HMAC_MD5, &key);
350    if (ret) {
351	kdc_log(context, config, 2,
352		"digest-request: user %s\\%s, missing NTLM key",
353		ntp.domain, ntp.user);
354	goto failed;
355    }
356
357    kdc_log(context, config, 2,
358	    "digest-request: found user, processing ntlm request");
359
360    if (ntq->ntChallengeResponse.length != 24) {
361	struct ntlm_buf targetinfo, answer;
362	unsigned char ntlmv2[16];
363
364	ntlm_version = "ntlmv2";
365
366	if (!gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V2, NULL)) {
367	    kdc_log(context, config, 2, "NTLMv2 disabled");
368	    ret = HNTLM_ERR_NOT_CONFIGURED;
369	    goto failed;
370	}
371
372	answer.length = ntq->ntChallengeResponse.length;
373	answer.data = ntq->ntChallengeResponse.data;
374
375	ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
376				     key->key.keyvalue.length,
377				     ntq->loginUserName,
378				     ntq->loginDomainName,
379				     0,
380				     ntq->lmchallenge.data,
381				     &answer,
382				     &targetinfo,
383				     ntlmv2);
384	if (ret) {
385	    if (clientdb->hdb_auth_status)
386		clientdb->hdb_auth_status(context, clientdb, user,
387					  HDB_AUTH_WRONG_PASSWORD);
388
389	    kdc_log(context, config, 2,
390		    "digest-request: verify ntlm2 hash failed: %d", ret);
391	    ret = HNTLM_ERR_INVALID_NTv2_ANSWER;
392	    goto failed;
393	}
394
395	if (clientdb->hdb_auth_status)
396	    clientdb->hdb_auth_status(context, clientdb, user,
397				      HDB_AUTH_SUCCESS);
398
399	/*
400	 * Build session key
401	 */
402	{
403	    size_t len = MIN(ntq->ntChallengeResponse.length, 16);
404	    CCHmacContext c;
405
406	    kdc_log(context, config, 2,
407		    "digest-request: basic key");
408
409	    CCHmacInit(&c, kCCHmacAlgMD5, ntlmv2, sizeof(ntlmv2));
410	    CCHmacUpdate(&c, ntq->ntChallengeResponse.data, len);
411	    CCHmacFinal(&c, sessionkey);
412	    memset(&c, 0, sizeof(c));
413	}
414
415	memset(ntlmv2, 0, sizeof(ntlmv2));
416
417	ret = validate_targetinfo(context, &targetinfo, ntq->t2targetname, ntq->acceptorUser,
418				  &ntp.avflags);
419	if (ret) {
420	    kdc_log(context, config, 2, "NTLMv2 targetinfo validation failed");
421	    goto failed;
422	}
423
424	ntp.targetinfo.data = targetinfo.data;
425	ntp.targetinfo.length = targetinfo.length;
426
427    } else {
428	struct ntlm_buf answer;
429	uint8_t challenge[8];
430	uint8_t usk[16];
431	CCDigestRef digest;
432
433	ntlm_version = "ntlmv1";
434
435	if (!gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V1, NULL)) {
436	    kdc_log(context, config, 2, "NTLMv1 disabled");
437	    ret = HNTLM_ERR_NOT_CONFIGURED;
438	    goto failed;
439	}
440
441	if (ntq->lmchallenge.length != 8) {
442	    ret = HNTLM_ERR_INVALID_CHALLANGE;
443	    goto failed;
444	}
445
446	if (ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) {
447	    unsigned char sessionhash[CC_MD5_DIGEST_LENGTH];
448
449	    /* the first first 8 bytes is the challenge, what is the other 16 bytes ? */
450	    if (ntq->lmChallengeResponse.length != 24) {
451		ret = HNTLM_ERR_INVALID_LMv2_RESPONSE;
452		goto failed;
453	    }
454
455	    digest = CCDigestCreate(kCCDigestMD5);
456	    CCDigestUpdate(digest, ntq->lmchallenge.data, 8);
457	    CCDigestUpdate(digest, ntq->lmChallengeResponse.data, 8);
458	    CCDigestFinal(digest, sessionhash);
459	    CCDigestDestroy(digest);
460	    memcpy(challenge, sessionhash, sizeof(challenge));
461	} else {
462	    memcpy(challenge, ntq->lmchallenge.data, ntq->lmchallenge.length);
463	}
464
465	ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
466					key->key.keyvalue.length,
467					challenge, &answer);
468	if (ret)
469	    goto failed;
470
471	if (ntq->ntChallengeResponse.length != answer.length ||
472	    memcmp(ntq->ntChallengeResponse.data, answer.data, answer.length) != 0) {
473	    free(answer.data);
474	    kdc_log(context, config, 2,
475		    "digest-request: verify ntlm1 hash failed");
476	    ret = HNTLM_ERR_INVALID_NTv1_ANSWER;
477	    goto failed;
478	}
479	free(answer.data);
480
481	digest = CCDigestCreate(kCCDigestMD4);
482	CCDigestUpdate(digest,
483		       key->key.keyvalue.data,
484		       key->key.keyvalue.length);
485	CCDigestFinal(digest, usk);
486	CCDigestDestroy(digest);
487
488
489	if ((ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) != 0) {
490	    CCHmacContext hc;
491
492	    CCHmacInit(&hc, kCCHmacAlgMD5, usk, sizeof(usk));
493	    CCHmacUpdate(&hc, ntq->lmchallenge.data, 8);
494	    CCHmacUpdate(&hc, ntq->lmChallengeResponse.data, 8);
495	    CCHmacFinal(&hc, sessionkey);
496	    memset(&hc, 0, sizeof(hc));
497	} else {
498	    memcpy(sessionkey, usk, sizeof(sessionkey));
499	}
500    }
501
502    if (ntq->ntlmFlags & NTLM_NEG_KEYEX) {
503	struct ntlm_buf base, enc, sess;
504
505	base.data = sessionkey;
506	base.length = sizeof(sessionkey);
507	enc.data = ntq->encryptedSessionKey.data;
508	enc.length = ntq->encryptedSessionKey.length;
509
510	ret = heim_ntlm_keyex_unwrap(&base, &enc, &sess);
511	if (ret != 0)
512	    goto failed;
513	if (sess.length != sizeof(sessionkey)) {
514	    heim_ntlm_free_buf(&sess);
515	    ret = HNTLM_ERR_INVALID_SESSIONKEY;
516	    goto failed;
517	}
518	memcpy(sessionkey, sess.data, sizeof(sessionkey));
519	heim_ntlm_free_buf(&sess);
520    }
521
522    sessionkeydata.data = sessionkey;
523    sessionkeydata.length = sizeof(sessionkey);
524    ntp.sessionkey = &sessionkeydata;
525
526    ntp.success = 1;
527    ntp.ntlmFlags = ntq->ntlmFlags;
528
529    (*response)(response_ctx, &ntp);
530
531failed:
532    if (user_name)
533	free(user_name);
534    log_complete(context, "kdc", &ntp, ret, ntlm_version);
535
536    if (ntp.targetinfo.data)
537	free(ntp.targetinfo.data);
538
539    if (user)
540	_kdc_free_ent (context, user);
541
542    if (client)
543	krb5_free_principal(context, client);
544
545    return ret;
546}
547
548/*
549 *
550 */
551
552#ifdef HAVE_OPENDIRECTORY
553
554struct ntlmod {
555    ODNodeRef node;
556};
557
558/*
559 *
560 */
561
562static CFArrayRef meta_keys;
563static char *ntlmDomain;
564
565static void
566update_ntlm(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
567{
568    CFDictionaryRef settings;
569    CFStringRef n;
570
571    if (store == NULL)
572	return;
573
574    settings = (CFDictionaryRef)SCDynamicStoreCopyValue(store, CFSTR("com.apple.smb"));
575    if (settings == NULL)
576	return;
577
578    n = CFDictionaryGetValue(settings, CFSTR("NetBIOSName"));
579    if (n == NULL || CFGetTypeID(n) != CFStringGetTypeID())
580	goto fin;
581
582    if (ntlmDomain)
583	free(ntlmDomain);
584    ntlmDomain = rk_cfstring2cstring(n);
585    strupr(ntlmDomain);
586
587fin:
588    CFRelease(settings);
589    return;
590}
591
592static void
593ntlm_notification(void)
594{
595    SCDynamicStoreRef store;
596    dispatch_queue_t queue;
597
598    store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("com.apple.Kerberos.gss-od"), update_ntlm, NULL);
599    if (store == NULL)
600	return;
601
602    CFTypeRef key[] = {CFSTR("com.apple.smb")};
603    CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, key, 1, NULL);
604    SCDynamicStoreSetNotificationKeys(store, keys, NULL);
605    CFRelease(keys);
606
607    queue = dispatch_queue_create("com.apple.gss-od.ntlm-name", NULL);
608    if (queue == NULL) {
609	CFRelease(store);
610	errx(1, "dispatch_queue_create");
611    }
612
613    SCDynamicStoreSetDispatchQueue(store, queue);
614    CFRelease(store);
615
616    dispatch_sync(queue, ^{ update_ntlm(store, NULL, NULL); });
617}
618
619
620static void
621load_meta_keys(void)
622{
623    CFMutableArrayRef keys;
624
625    keys = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
626    CFArrayAppendValue(keys, kODAttributeTypeMetaNodeLocation);
627    meta_keys = keys;
628}
629
630
631static int
632od_init(krb5_context context, void **ctx)
633{
634    static dispatch_once_t once;
635    struct ntlmod *c;
636
637    dispatch_once(&once, ^{
638	ntlm_notification();
639	load_meta_keys();
640    });
641
642    if (ntlmDomain == NULL)
643	return ENOENT;
644
645    c = calloc(1, sizeof(*c));
646    if (c == NULL)
647	return ENOMEM;
648
649    c->node = ODNodeCreateWithNodeType(kCFAllocatorDefault,
650				       kODSessionDefault,
651				       kODNodeTypeAuthentication, NULL);
652    if (c->node == NULL) {
653	free(c);
654	return ENOENT;
655    }
656
657    *ctx = c;
658
659    return 0;
660}
661
662
663static uint32_t
664od_flags(krb5_context context, void *ctx)
665{
666    CFArrayRef subnodes;
667    CFIndex count, n;
668    ODNodeRef node;
669
670    if (gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_SESSION_KEY, NULL))
671	return 0;
672
673#ifdef __APPLE_PRIVATE__
674    subnodes = ODSessionCopySessionKeySupport(kODSessionDefault);
675    if (subnodes) {
676	int haveLDAPSessionKeySupport = 0;
677
678	count = CFArrayGetCount(subnodes);
679	for (n = 0; n < count; n++) {
680	    CFStringRef name;
681
682	    node = (ODNodeRef)CFArrayGetValueAtIndex(subnodes, n);
683
684	    name = ODNodeGetName(node);
685	    if (name && !CFStringHasPrefix(name, CFSTR("/LDAPv3/"))) {
686		kdc_log(context, config, 2, "digest-request: have /LDAPv3/ nodes with signing");
687		haveLDAPSessionKeySupport = 1;
688		break;
689	    }
690	}
691	CFRelease(subnodes);
692
693	if (haveLDAPSessionKeySupport)
694	    return 0;
695
696	kdc_log(context, config, 2, "digest-request: have no LDAPv3 nodes with signing");
697
698    } else {
699	kdc_log(context, config, 2, "digest-request: have no nodes with signing");
700    }
701#endif
702
703    node = ODNodeCreateWithName(kCFAllocatorDefault, kODSessionDefault, CFSTR("/LDAPv3"), NULL);
704    if (node == NULL)
705	return 0;
706
707    subnodes = ODNodeCopySubnodeNames(node, NULL);
708    CFRelease(node);
709
710    if (subnodes == NULL)
711	return 0;
712
713    count = CFArrayGetCount(subnodes);
714    CFRelease(subnodes);
715    if (count == 0)
716	return 0;
717
718    /*
719     * Drat, we have OD configured. So no session key support, we must turn that off for everyone!
720     */
721
722    return NTLM_NEG_SIGN | NTLM_NEG_SEAL |
723	NTLM_NEG_ALWAYS_SIGN | NTLM_NEG_NTLM2_SESSION |
724	NTLM_NEG_KEYEX;
725}
726
727static int
728od_authenticate(void *ctx,
729		krb5_context context,
730		const NTLMRequest2 *ntq,
731		void *response_ctx,
732		void (response)(void *, const NTLMReply *ntp))
733{
734    struct ntlmod *c = ctx;
735    CFArrayRef outAuthItems = NULL;
736    CFMutableArrayRef authItems = NULL;
737    ODRecordRef record = NULL;
738    Boolean b;
739    CFStringRef username = NULL, domain = NULL;
740    CFErrorRef error = NULL;
741    CFMutableDataRef challenge = NULL, resp = NULL;
742    CFArrayRef values = NULL;
743    NTLMReply ntp;
744    int ret = HNTLM_ERR_NOT_CONFIGURED;
745
746    if (ntlmDomain == NULL || ctx == NULL) {
747	kdc_log(context, config, 2, "digest-request: no ntlmDomain");
748	return HNTLM_ERR_NOT_CONFIGURED;
749    }
750
751    memset(&ntp, 0, sizeof(ntp));
752
753    username = CFStringCreateWithCString(NULL, ntq->loginUserName,
754					 kCFStringEncodingUTF8);
755    if (username == NULL) {
756	ret = ENOMEM;
757	goto out;
758    }
759
760    challenge = CFDataCreateMutable(NULL, 0);
761    if (challenge == NULL) {
762	ret = ENOMEM;
763	goto out;
764    }
765    CFDataAppendBytes(challenge, ntq->lmchallenge.data, ntq->lmchallenge.length);
766
767    resp = CFDataCreateMutable(NULL, 0);
768    if (response == NULL) {
769	ret = ENOMEM;
770	goto out;
771    }
772    CFDataAppendBytes(resp, ntq->ntChallengeResponse.data, ntq->ntChallengeResponse.length);
773
774    domain = CFStringCreateWithCString(NULL, ntq->loginDomainName, kCFStringEncodingUTF8);
775    if (domain == NULL) {
776	ret = ENOMEM;
777	goto out;
778    }
779
780    authItems = CFArrayCreateMutable(NULL, 5, &kCFTypeArrayCallBacks);
781    if (authItems == NULL) {
782	ret = ENOMEM;
783	goto out;
784    }
785
786    CFArrayAppendValue(authItems, username);
787    CFArrayAppendValue(authItems, challenge);
788    CFArrayAppendValue(authItems, resp);
789    CFArrayAppendValue(authItems, username);
790    CFArrayAppendValue(authItems, domain);
791
792    record = ODNodeCopyRecord(c->node, kODRecordTypeUsers,
793			      username, meta_keys, &error);
794    if (record == NULL) {
795	kdc_log(context, config, 2, "digest-request: failed to find user %s in OD", ntq->loginUserName);
796	ret = ENOENT;
797	goto out;
798    }
799
800    values = ODRecordCopyValues(record, kODAttributeTypeMetaNodeLocation, NULL);
801    if (values && CFArrayGetCount(values) > 0) {
802	CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(values, 0);
803	if (CFStringGetTypeID() != CFGetTypeID(str)) {
804	    ret = HNTLM_ERR_NOT_CONFIGURED;
805	    goto out;
806	}
807	if (!CFStringHasPrefix(str, CFSTR("/LDAPv3/"))) {
808	    kdc_log(context, config, 2, "digest-request: user not in /LDAPv3");
809	    ret = ENOENT;
810	    goto out;
811	}
812    }
813
814    b = ODRecordVerifyPasswordExtended(record,
815				       kODAuthenticationTypeNTLMv2WithSessionKey,
816				       authItems, &outAuthItems, NULL,
817				       &error);
818
819    if (!b) {
820	kdc_log(context, config, 2, "digest-request: authentication failed");
821	ret = ENOENT;
822	goto out;
823    }
824
825    /*
826     * No ntlmv2 session key or keyex for OD.
827     */
828    ntp.ntlmFlags = ntq->ntlmFlags;
829    ntp.ntlmFlags &= ~(NTLM_NEG_KEYEX|NTLM_NEG_NTLM2_SESSION);
830
831    /*
832     * If the NTLMv2 session key is supported, it is returned in the
833     * output buffer
834     */
835
836
837    if (outAuthItems && CFArrayGetCount(outAuthItems) > 0) {
838	CFDataRef data = (CFDataRef)CFArrayGetValueAtIndex(outAuthItems, 0);
839	if (CFGetTypeID(data) == CFDataGetTypeID() && CFDataGetLength(data) >= 16) {
840	    ntp.sessionkey = calloc(1, sizeof(*ntp.sessionkey));
841	    if (ntp.sessionkey == NULL) {
842		ret = ENOMEM;
843		goto out;
844	    }
845	    krb5_data_copy(ntp.sessionkey, CFDataGetBytePtr(data), 16);
846	    kdc_log(context, config, 10, "OD auth with session key");
847	}
848    }
849
850    /*
851     * If no session key was passed back, strip of SIGN/SEAL
852     */
853
854    if (ntp.sessionkey == NULL || ntp.sessionkey->length == 0)
855	ntp.ntlmFlags &= ~(NTLM_NEG_SIGN|NTLM_NEG_SEAL|NTLM_NEG_ALWAYS_SIGN);
856
857
858    ntp.user = ntq->loginUserName;
859    ntp.domain = ntlmDomain;
860
861    ntp.success = 1;
862    response(response_ctx, &ntp);
863    ret = 0;
864
865out:
866    log_complete(context, "od", &ntp, ret, derive_version_ntq(ntq));
867
868    if (values)
869	CFRelease(values);
870    if (error)
871	CFRelease(error);
872    if (challenge)
873	CFRelease(challenge);
874    if (resp)
875	CFRelease(resp);
876    if (username)
877	CFRelease(username);
878    if (domain)
879	CFRelease(domain);
880    if (authItems)
881	CFRelease(authItems);
882    if (outAuthItems)
883	CFRelease(outAuthItems);
884    if (record)
885	CFRelease(record);
886
887    return ret;
888}
889
890#endif
891
892/*
893 *
894 */
895
896static uint32_t
897guest_flags(krb5_context context, void *ctx)
898{
899    return 0;
900}
901
902static int
903guest_authenticate(void *ctx,
904		   krb5_context context,
905		   const NTLMRequest2 *ntq,
906		   void *response_ctx,
907		   void (response)(void *, const NTLMReply *ntp))
908{
909    unsigned char sessionkey[16];
910    krb5_data sessionkeydata;
911    NTLMReply ntp;
912    int is_guest = (strcasecmp("GUEST", ntq->loginUserName) == 0);
913    int ret = HNTLM_ERR_INVALID_NO_GUEST;
914
915    memset(&ntp, 0, sizeof(ntp));
916
917    if (is_guest || (ntq->ntlmFlags & NTLM_NEG_ANONYMOUS)) {
918	kdc_log(context, config, 2,
919		"digest-request: found guest, let in");
920	ntp.avflags |= NTLM_TI_AV_FLAG_GUEST;
921	ntp.user = "GUEST";
922
923#ifdef HAVE_OPENDIRECTORY
924	ntp.domain = ntlmDomain;
925#endif
926	if (ntp.domain == NULL)
927	    ntp.domain = ntq->loginDomainName;
928
929	if (ntp.user == NULL || ntp.domain == NULL)
930	    goto out;
931
932	if (is_guest) {
933	    /* no password */
934	    CCDigest(kCCDigestMD4, (void *)"", 0, sessionkey);
935	}
936    } else {
937	ret = HNTLM_ERR_INVALID_NO_GUEST;
938	goto out;
939    }
940
941    if ((ntq->ntlmFlags & NTLM_NEG_ANONYMOUS)) {
942
943    } else if (ntq->ntChallengeResponse.length != 24) {
944	size_t len = MIN(ntq->ntChallengeResponse.length, 16);
945	struct ntlm_buf targetinfo, answer;
946	uint8_t ntlmv2[16];
947	CCHmacContext c;
948
949	answer.length = ntq->ntChallengeResponse.length;
950	answer.data = ntq->ntChallengeResponse.data;
951
952	ret = heim_ntlm_verify_ntlm2(sessionkey,
953				     sizeof(sessionkey),
954				     ntq->loginUserName,
955				     ntq->loginDomainName,
956				     0,
957				     ntq->lmchallenge.data,
958				     &answer,
959				     &targetinfo,
960				     ntlmv2);
961	if (ret) {
962	    kdc_log(context, config, 2,
963		    "digest-request: guest authentication failed: %d", ret);
964	    goto out;
965	}
966
967	free(targetinfo.data);
968
969	kdc_log(context, config, 2,
970		"digest-request: basic key");
971
972	CCHmacInit(&c, kCCHmacAlgMD5, ntlmv2, sizeof(ntlmv2));
973	CCHmacUpdate(&c, ntq->ntChallengeResponse.data, len);
974	CCHmacFinal(&c, sessionkey);
975	memset(&c, 0, sizeof(c));
976    } else {
977	CCDigestRef digest;
978	uint8_t usk[16];
979
980	digest = CCDigestCreate(kCCDigestMD4);
981	CCDigestUpdate(digest, sessionkey, sizeof(sessionkey));
982	CCDigestFinal(digest, usk);
983	CCDigestDestroy(digest);
984
985	if ((ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) != 0) {
986	    CCHmacContext hc;
987
988	    CCHmacInit(&hc, kCCHmacAlgMD5, usk, sizeof(usk));
989	    CCHmacUpdate(&hc, ntq->lmchallenge.data, 8);
990	    CCHmacUpdate(&hc, ntq->lmChallengeResponse.data, 8);
991	    CCHmacFinal(&hc, sessionkey);
992	    memset(&hc, 0, sizeof(hc));
993	} else {
994	    memcpy(sessionkey, usk, sizeof(sessionkey));
995	}
996
997    }
998
999    if (ntq->ntlmFlags & NTLM_NEG_KEYEX) {
1000	struct ntlm_buf base, enc, sess;
1001
1002	base.data = sessionkey;
1003	base.length = sizeof(sessionkey);
1004	enc.data = ntq->encryptedSessionKey.data;
1005	enc.length = ntq->encryptedSessionKey.length;
1006
1007	ret = heim_ntlm_keyex_unwrap(&base, &enc, &sess);
1008	if (ret != 0)
1009	    goto out;
1010	if (sess.length != sizeof(sessionkey)) {
1011	    heim_ntlm_free_buf(&sess);
1012	    ret = HNTLM_ERR_INVALID_SESSIONKEY;
1013	    goto out;
1014	}
1015	memcpy(sessionkey, sess.data, sizeof(sessionkey));
1016	heim_ntlm_free_buf(&sess);
1017    }
1018
1019    sessionkeydata.data = sessionkey;
1020    sessionkeydata.length = sizeof(sessionkey);
1021    ntp.sessionkey = &sessionkeydata;
1022
1023    ntp.ntlmFlags = ntq->ntlmFlags;
1024    ntp.success = 1;
1025
1026    response(response_ctx, &ntp);
1027    ret = 0;
1028out:
1029    log_complete(context, "guest", &ntp, ret, derive_version_ntq(ntq));
1030
1031    return ret;
1032}
1033
1034#ifdef HAVE_APPLE_NETLOGON
1035
1036#define LOGON_USER				0x00000001L
1037#define LOGON_NOENCRYPTION			0x00000002L
1038#define LOGON_CACHED_ACCOUNT			0x00000004L
1039#define LOGON_USED_LM_PASSWORD			0x00000008L
1040#define LOGON_EXTRA_SIDS			0x00000020L
1041#define LOGON_SUBAUTH_SESSION_KEY		0x00000040L
1042#define LOGON_SERVER_TRUST_ACCOUNT		0x00000080L
1043#define LOGON_NTLMV2_ENABLED			0x00000100L
1044#define LOGON_RESOURCE_GROUPS			0x00000200L
1045#define LOGON_PROFILE_PATH_RETURNED		0x00000400L
1046#define LOGON_NT_V2				0x00000800L
1047#define LOGON_LM_V2				0x00001000L
1048#define LOGON_NTLM_V2				0x00002000L
1049
1050#define NTLM_LOGON_REQ_VERSION    1
1051
1052typedef struct _NTLM_LOGON_REQ {
1053    uint32_t Version;
1054    const char *LogonDomainName;
1055    const char *UserName;
1056    const char *Workstation;
1057    uint8_t LmChallenge[8];
1058    const uint8_t *NtChallengeResponse;
1059    uint16_t NtChallengeResponseLength;
1060    const uint8_t *LmChallengeResponse;
1061    uint16_t LmChallengeResponseLength;
1062} NTLM_LOGON_REQ, *PNTLM_LOGON_REQ;
1063
1064typedef uint32_t
1065(*NTLM_LOGON_FN)(const NTLM_LOGON_REQ *LogonReq,
1066		 uint8_t **pAuthData,
1067		 size_t *pAuthDataSize,
1068		 char **pAccountName,
1069		 char **pAccountDomain,
1070		 uint8_t SessionKey[16],
1071		 uint32_t *Flags);
1072
1073typedef enum _NETLOGON_PROBE_STATUS {
1074    NETLOGON_PROBE_UNAVAILABLE = -1,    /* Could not reach RPC server */
1075    NETLOGON_PROBE_NOT_CONFIGURED = 0,  /* RPC server available but not joined to domain */
1076    NETLOGON_PROBE_CONFIGURED = 1,      /* RPC server available and joined to domain */
1077    NETLOGON_PROBE_CONNECTED = 2        /* RPC server available and secure channel up */
1078} NETLOGON_PROBE_STATUS;
1079
1080typedef NETLOGON_PROBE_STATUS
1081(*NET_LOGON_PROBE_FN)(const char *, char **);
1082
1083/*
1084 *
1085 */
1086
1087struct ntlmnetr {
1088    OM_uint32 flags;
1089    char *domain;
1090};
1091
1092/*
1093 *
1094 */
1095
1096static char *netlogonDomain;
1097static char *netlogonServer;
1098
1099
1100static void
1101update_domain(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
1102{
1103    CFDictionaryRef settings;
1104    CFStringRef n;
1105    size_t len;
1106
1107    if (store == NULL)
1108	return;
1109
1110    settings = (CFDictionaryRef)SCDynamicStoreCopyValue(store, CFSTR("com.apple.opendirectoryd.ActiveDirectory"));
1111    if (settings == NULL)
1112	return;
1113
1114    n = CFDictionaryGetValue(settings, CFSTR("DomainNameFlat"));
1115    if (n == NULL || CFGetTypeID(n) != CFStringGetTypeID())
1116	goto fin;
1117
1118    if (netlogonDomain)
1119	free(netlogonDomain);
1120    netlogonDomain = rk_cfstring2cstring(n);
1121
1122    n = CFDictionaryGetValue(settings, CFSTR("TrustAccount"));
1123    if (n == NULL || CFGetTypeID(n) != CFStringGetTypeID())
1124	goto fin;
1125
1126    if (netlogonServer)
1127	free(netlogonServer);
1128    netlogonServer = rk_cfstring2cstring(n);
1129
1130    len = strlen(netlogonServer);
1131    if (len > 1 && netlogonServer[len - 1] == '$')
1132	netlogonServer[len - 1] = '\0';
1133
1134    strupr(netlogonServer);
1135
1136fin:
1137    CFRelease(settings);
1138    return;
1139}
1140
1141
1142static void
1143domain_notification(void)
1144{
1145    SCDynamicStoreRef store;
1146    dispatch_queue_t queue;
1147
1148    store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("com.apple.Kerberos.gss-netlogon"), update_domain, NULL);
1149    if (store == NULL)
1150	return;
1151
1152    CFTypeRef key[] = {CFSTR("com.apple.opendirectoryd.ActiveDirectory")};
1153    CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, key, 1, NULL);
1154    SCDynamicStoreSetNotificationKeys(store, keys, NULL);
1155    CFRelease(keys);
1156
1157    queue = dispatch_queue_create("com.apple.gss-netlogon.ntlm-name", NULL);
1158    if (queue == NULL) {
1159	CFRelease(store);
1160	errx(1, "dispatch_queue_create");
1161    }
1162
1163    SCDynamicStoreSetDispatchQueue(store, queue);
1164
1165    dispatch_sync(queue, ^{ update_domain(store, NULL, NULL); });
1166}
1167
1168
1169/*
1170 *
1171 */
1172
1173static void *nt_framework_handle = NULL;
1174static NET_LOGON_PROBE_FN netLogonProbe = NULL;
1175static NTLM_LOGON_FN nTLMLogon = NULL;
1176
1177#define NT_FRAMEWORK_PATH "/System/Library/PrivateFrameworks/nt.framework/nt"
1178
1179static void
1180init_nt_framework(void *ctx)
1181{
1182    nt_framework_handle = dlopen(NT_FRAMEWORK_PATH, RTLD_LOCAL);
1183    if (nt_framework_handle == NULL)
1184	return;
1185
1186    netLogonProbe = dlsym(nt_framework_handle, "NetLogonProbe");
1187    nTLMLogon = dlsym(nt_framework_handle, "NTLMLogon");
1188
1189
1190    if (netLogonProbe && nTLMLogon) {
1191	domain_notification();
1192    }
1193}
1194
1195
1196
1197static int
1198netr_init(krb5_context context, void **ctx)
1199{
1200    static heim_base_once_t init_once = HEIM_BASE_ONCE_INIT;
1201    NETLOGON_PROBE_STATUS probeStatus;
1202    struct ntlmnetr *c;
1203    char *domain;
1204
1205    heim_base_once_f(&init_once, NULL, init_nt_framework);
1206
1207    if (netLogonProbe == NULL || nTLMLogon == NULL)
1208	return ENOENT;
1209
1210    probeStatus = netLogonProbe(NULL, &domain);
1211    kdc_log(context, config, 1, "digest-request: netr probe %d", (int)probeStatus);
1212    switch (probeStatus) {
1213    case NETLOGON_PROBE_CONFIGURED:
1214    case NETLOGON_PROBE_CONNECTED:
1215	break;
1216    default:
1217	return ENOENT;
1218    }
1219
1220    c = calloc(1, sizeof(*c));
1221    if (c == NULL) {
1222	free(domain);
1223	return ENOMEM;
1224    }
1225    c->domain = domain;
1226
1227    *ctx = c;
1228
1229    return GSS_S_COMPLETE;
1230}
1231
1232static uint32_t
1233netr_flags(krb5_context context, void *ctx)
1234{
1235    return 0;
1236}
1237
1238static int
1239netr_ti(void *ctx, struct ntlm_targetinfo *ti)
1240{
1241    if (netlogonServer == NULL || netlogonDomain == NULL)
1242	return HNTLM_ERR_NO_NETR_CONFIGURED;
1243
1244    if ((ti->servername = strdup(netlogonServer)) == NULL)
1245	return ENOMEM;
1246
1247    if ((ti->domainname = strdup(netlogonDomain)) == NULL) {
1248	free(ti->servername);
1249	return ENOMEM;
1250    }
1251
1252    return 0;
1253}
1254
1255/*
1256 *
1257 */
1258
1259static int
1260netr_authenticate(void *ctx,
1261		  krb5_context context,
1262		  const NTLMRequest2 *ntq,
1263		  void *response_ctx,
1264		  void (response)(void *, const NTLMReply *ntp))
1265{
1266    struct ntlmnetr *c = ctx;
1267    OM_uint32 ret;
1268    NTLM_LOGON_REQ logonReq;
1269    krb5_data sessionkeydata;
1270    uint8_t sessionkey[16];
1271    uint32_t userFlags;
1272    int ntlm2;
1273    NTLMReply ntp;
1274    krb5_data pac;
1275
1276    if (netlogonDomain == NULL || netlogonServer == NULL || ctx == NULL)
1277	return HNTLM_ERR_NO_NETR_CONFIGURED;
1278
1279    memset(&ntp, 0, sizeof(ntp));
1280
1281    krb5_data_zero(&sessionkeydata);
1282
1283    logonReq.Version = NTLM_LOGON_REQ_VERSION;
1284    logonReq.LogonDomainName = ntq->loginDomainName;
1285    logonReq.UserName = ntq->loginUserName;
1286    logonReq.Workstation = ntq->workstation;
1287
1288    /*
1289     * From http://davenport.sourceforge.net/ntlm.html:
1290     *
1291     *  "Instead of sending the server challenge directly
1292     *  over the NetLogon pipe to the domain controller,
1293     *  the server sends the MD5 hash of the server challenge
1294     *  concatenated with the client nonce (lifted from the LM
1295     *  response field)."
1296     *
1297     * Unfortunately, the client doesn't give us any indication
1298     * whether it's using NTLMv1 with NTLM2 security or rather
1299     * NTLMv2. It appears we can only determine this from the
1300     * response length, which will be >24 for NTLMv2.
1301     */
1302    ntlm2 = (ntq->ntlmFlags & NTLM_NEG_NTLM2_SESSION) &&
1303            (ntq->lmChallengeResponse.length >= 8) &&
1304            (ntq->ntChallengeResponse.length == 24);
1305
1306    if (ntlm2) {
1307        ret = heim_ntlm_calculate_ntlm2_sess_hash(ntq->lmChallengeResponse.data,
1308                                                  ntq->lmchallenge.data,
1309                                                  logonReq.LmChallenge);
1310        if (ret)
1311	    goto out;
1312    } else {
1313        memcpy(logonReq.LmChallenge, ntq->lmchallenge.data, 8);
1314        c->flags &= ~(LOGON_NTLMV2_ENABLED);
1315    }
1316
1317    logonReq.NtChallengeResponseLength = ntq->ntChallengeResponse.length;
1318    logonReq.NtChallengeResponse = ntq->ntChallengeResponse.data;
1319    logonReq.LmChallengeResponseLength = ntq->lmChallengeResponse.length;
1320    logonReq.LmChallengeResponse = ntq->lmChallengeResponse.data;
1321
1322    ret = nTLMLogon(&logonReq,
1323		    (uint8_t **)&pac.data,
1324		    &pac.length,
1325		    &ntp.user,
1326		    &ntp.domain,
1327		    sessionkey,
1328		    &userFlags);
1329    if (ret) {
1330	kdc_log(context, config, 1, "digest-request netr: failed user=%s\\%s DC status code %08x",
1331		ntq->loginDomainName,
1332		ntq->loginUserName,
1333		ret);
1334	goto out;
1335    }
1336
1337    digest_debug_key(context, 10, "ntlm base session key",
1338		     sessionkey, sizeof(sessionkey));
1339
1340    /* XXX copy into ntp */
1341    free(pac.data);
1342
1343    if (ntlm2) {
1344        /* http://davenport.sourceforge.net/ntlm.html#theNtlm2SessionResponseUserSessionKey */
1345        heim_ntlm_derive_ntlm2_sess(sessionkey,
1346                                    ntq->lmChallengeResponse.data, 8,
1347                                    ntq->lmchallenge.data,
1348                                    sessionkey);
1349
1350	digest_debug_key(context, 10, "ntlmv2 session' key",
1351			 sessionkey, sizeof(sessionkey));
1352    }
1353
1354    ntp.ntlmFlags = ntq->ntlmFlags;
1355
1356    /*
1357     * If the client sent a NTLMv2 response (as opposed to a
1358     * NTLMv1 reponse with NTLM2 security), then we also need
1359     * to negotiate a NTLM2 session key.
1360     */
1361    if (userFlags & LOGON_NTLMV2_ENABLED)
1362	ntp.ntlmFlags |= NTLM_NEG_NTLM2_SESSION;
1363
1364    if (ntq->ntlmFlags & NTLM_NEG_KEYEX) {
1365	struct ntlm_buf base, enc, sess;
1366
1367	base.data = sessionkey;
1368	base.length = sizeof(sessionkey);
1369	enc.data = ntq->encryptedSessionKey.data;
1370	enc.length = ntq->encryptedSessionKey.length;
1371
1372	ret = heim_ntlm_keyex_unwrap(&base, &enc, &sess);
1373	if (ret != 0)
1374	    goto out;
1375	if (sess.length != sizeof(sessionkey)) {
1376	    heim_ntlm_free_buf(&sess);
1377	    ret = HNTLM_ERR_INVALID_SESSION_KEY;
1378	    goto out;
1379	}
1380	memcpy(sessionkey, sess.data, sizeof(sessionkey));
1381	heim_ntlm_free_buf(&sess);
1382
1383	digest_debug_key(context, 10, "kexex key",
1384			 sessionkey, sizeof(sessionkey));
1385    }
1386
1387    sessionkeydata.data = sessionkey;
1388    sessionkeydata.length = sizeof(sessionkey);
1389    ntp.sessionkey = &sessionkeydata;
1390
1391    ntp.success = 1;
1392
1393    response(response_ctx, &ntp);
1394    ret = 0;
1395
1396out:
1397    log_complete(context, "netr", &ntp, ret, derive_version_ntq(ntq));
1398
1399    if (ntp.user)
1400	free(ntp.user);
1401    if (ntp.domain)
1402	free(ntp.domain);
1403    return ret;
1404}
1405
1406#endif
1407
1408/*
1409 *
1410 */
1411
1412struct ntlm_ftable backends[] = {
1413#ifdef HAVE_APPLE_NETLOGON
1414    {
1415	NULL,
1416	netr_init,
1417	NULL,
1418	netr_ti,
1419	netr_flags,
1420	netr_authenticate
1421    },
1422#endif
1423#ifdef HAVE_OPENDIRECTORY
1424    {
1425	NULL,
1426	od_init,
1427	NULL,
1428	NULL,
1429	od_flags,
1430	od_authenticate
1431    },
1432#endif
1433    {
1434	NULL,
1435	NULL,
1436	NULL,
1437	NULL,
1438	kdc_flags,
1439	kdc_authenticate
1440    },
1441    {
1442	NULL,
1443	NULL,
1444	NULL,
1445	NULL,
1446	guest_flags,
1447	guest_authenticate
1448    }
1449};
1450
1451struct callback {
1452    heim_sipc_call cctx;
1453    heim_ipc_complete complete;
1454};
1455
1456static void
1457complete_callback(void *ctx, const NTLMReply *ntp)
1458{
1459    struct callback *c = ctx;
1460    heim_idata rep;
1461    size_t size = 0;
1462    int ret;
1463
1464    ASN1_MALLOC_ENCODE(NTLMReply, rep.data, rep.length, ntp, &size, ret);
1465    if (ret)
1466	return;
1467    if (rep.length != size)
1468	abort();
1469
1470    c->complete(c->cctx, ret, &rep);
1471
1472    free(rep.data);
1473}
1474
1475/*
1476 *
1477 */
1478
1479static void
1480fillin_hostinfo(struct ntlm_targetinfo *ti)
1481{
1482    char local_hostname[MAXHOSTNAMELEN], *p;
1483
1484    if (netlogonServer && ti->servername == NULL)
1485	ti->servername = strdup(netlogonServer);
1486
1487    if (gethostname(local_hostname, sizeof(local_hostname)) < 0)
1488	return;
1489    local_hostname[sizeof(local_hostname) - 1] = '\0';
1490
1491    /* make up a server name */
1492    ti->dnsservername = strdup(local_hostname);
1493
1494    p = strchr(local_hostname, '.');
1495    if (p) {
1496	*p = '\0';
1497	p++;
1498    }
1499    if (ti->servername == NULL) {
1500	strupr(local_hostname);
1501	ti->servername = strdup(local_hostname);
1502    }
1503
1504    if (p && p[0])
1505	ti->dnsdomainname = strdup(p);
1506}
1507
1508
1509/*
1510 * Given the configuration, try to return the NTLM domain that is our
1511 * default DOMAIN.
1512 *
1513 * If we are not bound, pick the first in realm configuration that
1514 * matches the users preference.
1515 *
1516 * If we find no configuration, just return BUILTIN.
1517 */
1518
1519static krb5_error_code
1520get_ntlm_domain(krb5_context context,
1521		krb5_kdc_configuration *lconfig,
1522		const char *indomain,
1523		struct ntlm_targetinfo *ti)
1524{
1525    char *domain = NULL;
1526    krb5_error_code ret;
1527    unsigned int i;
1528
1529    for (i = 0; i < sizeof(backends) / sizeof(backends[0]); i++) {
1530	if (backends[i].ti == NULL)
1531	    continue;
1532
1533	memset(ti, 0, sizeof(*ti));
1534	ret = backends[i].ti(backends[i].ctx, ti);
1535	if (ret)
1536	    continue;
1537
1538	if (indomain == NULL || strcmp(indomain, ti->domainname) == 0)
1539	    goto dnsinfo;
1540
1541	heim_ntlm_free_targetinfo(ti);
1542    }
1543
1544    for (i = 0; i < lconfig->num_db; i++) {
1545	if (lconfig->db[i]->hdb_get_ntlm_domain == NULL)
1546	    continue;
1547	ret = lconfig->db[i]->hdb_get_ntlm_domain(context,
1548						 lconfig->db[i],
1549						 &domain);
1550	if (ret == 0 && domain)
1551	    break;
1552    }
1553    if (domain == NULL)
1554	domain = strdup("BUILTIN");
1555
1556    memset(ti, 0, sizeof(*ti));
1557
1558    ti->domainname = domain;
1559
1560 dnsinfo:
1561
1562    fillin_hostinfo(ti);
1563
1564    return 0;
1565}
1566
1567
1568/*
1569 * process type1/init request
1570 */
1571
1572static void
1573process_NTLMInit(krb5_context context,
1574		 NTLMInit *ni,
1575		 heim_ipc_complete complete,
1576		 heim_sipc_call cctx)
1577{
1578    heim_idata rep = { 0, NULL };
1579    struct ntlm_targetinfo ti;
1580    NTLMInitReply ir;
1581    char *indomain = NULL;
1582    size_t size = 0, n;
1583    int ret = 0;
1584
1585    memset(&ir, 0, sizeof(ir));
1586    memset(&ti, 0, sizeof(ti));
1587
1588    kdc_log(context, config, 1, "digest-request: init request");
1589
1590    ir.ntlmNegFlags = 0;
1591    for (n = 0; n < sizeof(backends) / sizeof(backends[0]); n++)
1592	ir.ntlmNegFlags |= backends[n].filter_flags(context, backends[n].ctx);
1593
1594    if (ni->domain && (*ni->domain)[0] != '\0')
1595	indomain = *ni->domain;
1596
1597    ret = get_ntlm_domain(context, config, indomain, &ti);
1598    if (ret)
1599	goto failed;
1600
1601    ti.timestamp = heim_ntlm_unix2ts_time(time(NULL));
1602
1603    kdc_log(context, config, 1, "digest-request: init return domain: %s server: %s indomain was: %s",
1604	    ti.domainname, ti.servername, indomain ? indomain : "<NULL>");
1605
1606    {
1607	struct ntlm_buf d;
1608
1609	ret = heim_ntlm_encode_targetinfo(&ti, 1, &d);
1610	if (ret)
1611	    goto failed;
1612	ir.targetinfo.data = d.data;
1613	ir.targetinfo.length = d.length;
1614    }
1615
1616    ASN1_MALLOC_ENCODE(NTLMInitReply, rep.data, rep.length, &ir, &size, ret);
1617    free_NTLMInitReply(&ir);
1618    if (ret)
1619	goto failed;
1620    if (rep.length != size)
1621	abort();
1622
1623failed:
1624    if (ret)
1625	kdc_log(context, config, 1, "digest-request: %d", ret);
1626
1627    heim_ntlm_free_targetinfo(&ti);
1628
1629    (*complete)(cctx, ret, &rep);
1630    free(rep.data);
1631
1632    return;
1633}
1634
1635/*
1636 *
1637 */
1638
1639static void
1640ntlm_service(void *ctx, const heim_idata *req,
1641	     const heim_icred cred,
1642	     heim_ipc_complete complete,
1643	     heim_sipc_call cctx)
1644{
1645    krb5_context context = ctx;
1646    static dispatch_once_t once;
1647    NTLMRequest2 ntq;
1648    NTLMInit ni;
1649
1650    kdc_log(context, config, 1, "digest-request: uid=%d",
1651	    (int)heim_ipc_cred_get_uid(cred));
1652
1653    if (heim_ipc_cred_get_uid(cred) != 0) {
1654	(*complete)(cctx, EPERM, NULL);
1655	return;
1656    }
1657
1658    dispatch_once(&once, ^{
1659	size_t n;
1660	for (n = 0; n < sizeof(backends) / sizeof(backends[0]); n++)
1661	    if (backends[n].init)
1662		backends[n].init(context, &backends[n].ctx);
1663    });
1664
1665    /* check if its a type1/init request */
1666    if (decode_NTLMInit(req->data, req->length, &ni, NULL) == 0) {
1667        process_NTLMInit(context, &ni, complete, cctx);
1668	free_NTLMInit(&ni);
1669    } else if (decode_NTLMRequest2(req->data, req->length, &ntq, NULL) == 0) {
1670	struct callback c = {
1671	    .complete = complete,
1672	    .cctx = cctx
1673	};
1674	size_t n;
1675	int ret = EPERM;
1676
1677	for (n = 0; n < sizeof(backends) / sizeof(backends[0]); n++) {
1678	    ret = backends[n].authenticate(backends[n].ctx, context, &ntq, &c, complete_callback);
1679	    if (ret == 0)
1680		break;
1681	}
1682	free_NTLMRequest2(&ntq);
1683	if (ret)
1684	    (*complete)(cctx, ret, NULL);
1685    }
1686}
1687
1688
1689#endif
1690
1691static struct getargs args[] = {
1692#ifdef __APPLE__
1693    {	"sandbox",	0, 	arg_negative_flag, &sandbox_flag,
1694	"use sandbox or not"
1695    },
1696#endif /* __APPLE__ */
1697    {	"unix",		0, 	arg_flag, &use_unix_flag,
1698	"support unix sockets"
1699    },
1700    {	"help",		'h',	arg_flag,   &help_flag },
1701    {	"version",	'v',	arg_flag,   &version_flag }
1702};
1703
1704static int num_args = sizeof(args) / sizeof(args[0]);
1705
1706static void
1707usage(int ret)
1708{
1709    arg_printusage (args, num_args, NULL, "");
1710    exit (ret);
1711}
1712
1713
1714int
1715main(int argc, char **argv)
1716{
1717    krb5_context context;
1718    int ret, optidx = 0;
1719
1720    setprogname(argv[0]);
1721
1722    __gss_ntlm_is_digest_service = 1;
1723
1724    if (getarg(args, num_args, argc, argv, &optidx))
1725	usage(1);
1726
1727    if (help_flag)
1728	usage(0);
1729
1730    if (version_flag) {
1731	print_version(NULL);
1732	exit(0);
1733    }
1734
1735    ret = krb5_init_context(&context);
1736    if (ret)
1737	krb5_errx(context, 1, "krb5_init_context");
1738
1739    ret = krb5_kdc_get_config(context, &config);
1740    if (ret)
1741	krb5_err(context, 1, ret, "krb5_kdc_default_config");
1742
1743    kdc_openlog(context, "digest-service", config);
1744
1745    ret = krb5_kdc_set_dbinfo(context, config);
1746    if (ret)
1747	krb5_err(context, 1, ret, "krb5_kdc_set_dbinfo");
1748
1749#ifdef __APPLE__
1750    if (sandbox_flag) {
1751	char *errorstring;
1752	ret = sandbox_init("kdc", SANDBOX_NAMED, &errorstring);
1753	if (ret)
1754	    errx(1, "sandbox_init failed: %d: %s", ret, errorstring);
1755    }
1756#endif
1757
1758#ifdef ENABLE_NTLM
1759#if __APPLE__
1760    {
1761	heim_sipc mach;
1762	heim_sipc_launchd_mach_init("org.h5l.ntlm-service",
1763				    ntlm_service, context, &mach);
1764    }
1765#endif
1766    if (use_unix_flag) {
1767	heim_sipc un;
1768	heim_sipc_service_unix("org.h5l.ntlm-service", ntlm_service, NULL, &un);
1769    }
1770#endif
1771    heim_sipc_timeout(60);
1772
1773    heim_ipc_main();
1774}
1775