1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "NetworkAuthenticationHelper.h"
25#include "KerberosHelper.h"
26
27#include <err.h>
28
29#define IS_LOVE 0
30
31static void
32lkdc_classic(void)
33{
34    CFArrayRef array;
35    CFIndex n;
36    NAHRef na;
37
38    CFShow(CFSTR("lkdc_classic"));
39
40    na = NAHCreate(NULL, CFSTR("localhost.local"), CFSTR("host"), NULL);
41    if (na == NULL)
42	errx(1, "NACreate");
43
44    array = NAHGetSelections(na);
45
46    for (n = 0; n < CFArrayGetCount(array); n++)
47	CFShow(CFArrayGetValueAtIndex(array, n));
48
49    CFRelease(na);
50}
51
52uint8_t token[] =
53    "\x60\x66\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x5c"
54    "\x30\x5a\xa0\x2c\x30\x2a\x06\x09\x2a\x86\x48\x82"
55    "\xf7\x12\x01\x02\x02\x06\x09\x2a\x86\x48\x86\xf7"
56    "\x12\x01\x02\x02\x06\x0a\x2b\x06\x01\x04\x01\x82"
57    "\x37\x02\x02\x0a\x06\x06\x2b\x05\x01\x05\x02\x07"
58    "\xa3\x2a\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f"
59    "\x64\x65\x66\x69\x6e\x65\x64\x5f\x69\x6e\x5f\x52"
60    "\x46\x43\x34\x31\x37\x38\x40\x70\x6c\x65\x61\x73"
61    "\x65\x5f\x69\x67\x6e\x6f\x72\x65";
62
63static void
64lkdc_wellknown(void)
65{
66    CFMutableDictionaryRef info;
67    CFDictionaryRef negToken;
68    CFArrayRef array;
69    CFDataRef serverToken;
70    CFIndex n;
71    NAHRef na;
72
73    CFShow(CFSTR("lkdc_wellknown"));
74
75    serverToken = CFDataCreateWithBytesNoCopy(NULL,
76					      token, sizeof(token) - 1,
77					      kCFAllocatorNull);
78
79
80    negToken = KRBDecodeNegTokenInit(NULL, serverToken);
81    CFRelease(serverToken);
82
83    info = CFDictionaryCreateMutable(NULL,
84				     0,
85				     &kCFTypeDictionaryKeyCallBacks,
86				     &kCFTypeDictionaryValueCallBacks);
87
88    CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
89    CFRelease(negToken);
90
91
92    na = NAHCreate(NULL, CFSTR("localhost.local"), kNAHServiceAFPServer, info);
93    CFRelease(info);
94    if (na == NULL)
95	errx(1, "NACreate");
96
97    array = NAHGetSelections(na);
98
99    for (n = 0; n < CFArrayGetCount(array); n++)
100	CFShow(CFArrayGetValueAtIndex(array, n));
101
102    CFRelease(na);
103}
104
105static void
106test_ntlm(void)
107{
108    CFMutableDictionaryRef info;
109    CFDictionaryRef negToken;
110    CFArrayRef array;
111    CFIndex n;
112    NAHRef na;
113
114    CFShow(CFSTR("test_ntlm"));
115
116    negToken = KRBCreateNegTokenLegacyNTLM(NULL);
117    if (negToken == NULL)
118	errx(1, "KRBCreateNegTokenLegacyNTLM");
119
120    info = CFDictionaryCreateMutable(NULL,
121				     0,
122				     &kCFTypeDictionaryKeyCallBacks,
123				     &kCFTypeDictionaryValueCallBacks);
124
125    CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
126    CFRelease(negToken);
127
128
129    na = NAHCreate(NULL, CFSTR("localhost.local"), CFSTR("host"), info);
130    CFRelease(info);
131    if (na == NULL)
132	errx(1, "NACreate");
133
134    array = NAHGetSelections(na);
135
136    for (n = 0; n < CFArrayGetCount(array); n++) {
137	NAHSelectionRef s = (void *)CFArrayGetValueAtIndex(array, n);
138	CFShow(s);
139
140	if (CFBooleanGetValue(NAHSelectionGetInfoForKey(s, kNAHUseSPNEGO)))
141	    errx(1, "not raw NTLM");
142    }
143
144    CFRelease(na);
145}
146
147static void
148kerberos_classic(CFStringRef hostname,
149		 CFStringRef service,
150		 CFStringRef user,
151		 CFStringRef password)
152{
153    CFMutableDictionaryRef info;
154    CFDictionaryRef negToken;
155    CFArrayRef array;
156    CFIndex n;
157    NAHRef na;
158
159    CFShow(CFSTR("test KRBCreateNegTokenLegacyKerberos"));
160
161    negToken = KRBCreateNegTokenLegacyKerberos(NULL);
162    if (negToken == NULL)
163	errx(1, "KRBCreateNegTokenLegacyKerberos");
164
165    CFShow(CFSTR("kerberos_classic"));
166
167    info = CFDictionaryCreateMutable(NULL,
168				     0,
169				     &kCFTypeDictionaryKeyCallBacks,
170				     &kCFTypeDictionaryValueCallBacks);
171
172    CFDictionaryAddValue(info, kNAHUserName, user);
173    CFDictionaryAddValue(info, kNAHPassword, password);
174
175    CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
176    CFRelease(negToken);
177
178
179    na = NAHCreate(NULL, hostname, service, info);
180    if (na == NULL)
181	errx(1, "NAHCreate");
182
183    array = NAHGetSelections(na);
184
185    for (n = 0; n < CFArrayGetCount(array); n++) {
186	NAHSelectionRef s = (void *)CFArrayGetValueAtIndex(array, n);
187	CFErrorRef error = NULL;
188	Boolean ret;
189
190	ret = NAHSelectionAcquireCredential(s, NULL, &error);
191	printf("NAHSelectionAcquireCredential: %s\n", ret ? "yes" : "no");
192	CFShow(s);
193	if (error)
194	    CFShow(error);
195
196	if (ret) {
197	    CFDictionaryRef authinfo = NAHSelectionCopyAuthInfo(s);
198	    if (authinfo) {
199		CFShow(CFSTR("Authinfo"));
200		CFShow(authinfo);
201		CFRelease(authinfo);
202	    }
203
204	    /* if NAHCopyReferenceKey returns NULL, refcounting not supported */
205
206	    CFStringRef refinfo = NAHCopyReferenceKey(s);
207
208	    CFStringRef label = CFSTR("fs:/testing");
209
210	    NAHAddReferenceAndLabel(s, label);
211
212	    NAHCredRemoveReference(refinfo);
213
214	    NAHCredAddReference(refinfo);
215	    NAHCredRemoveReference(refinfo);
216
217	    if (refinfo)
218		CFRelease(refinfo);
219
220	    /* should delete */
221	    NAHFindByLabelAndRelease(label);
222	}
223    }
224
225    CFRelease(na);
226
227    CFRelease(info);
228}
229
230/*
231 *
232 */
233
234static void
235check_valid(bool expected, bool found, const char *check)
236{
237    if (expected ^ found)
238	errx(1, "%s doesn't have expected result: %s %sfound",
239	     check, check, found ? "" : "not ");
240}
241
242
243static void
244verify_result(bool expect_mech_kerberos,
245	      bool expect_ntlm,
246	      bool expect_iakerb,
247	      bool expect_classic_kerberos,
248	      bool expect_classic_lkdc,
249	      bool expect_wlkdc,
250	      bool expect_raw_kerberos,
251	      CFStringRef service,
252	      CFStringRef hostname,
253	      CFStringRef username,
254	      CFStringRef password,
255	      void *spnego,
256	      size_t len,
257	      CFDictionaryRef negToken)
258{
259    CFDataRef serverToken = CFDataCreate(NULL, spnego, len);
260    CFMutableDictionaryRef info;
261    NAHRef na;
262
263    info = CFDictionaryCreateMutable(NULL,
264				     0,
265				     &kCFTypeDictionaryKeyCallBacks,
266				     &kCFTypeDictionaryValueCallBacks);
267
268    if (spnego) {
269	negToken = KRBDecodeNegTokenInit(NULL, serverToken);
270	CFRelease(serverToken);
271	if (negToken == NULL)
272	    errx(1, "failed to decode NegTokenInit");
273
274	CFShow(negToken);
275
276	CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
277	CFRelease(negToken);
278    } else if (negToken) {
279	CFShow(negToken);
280	CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
281    }
282    CFRelease(serverToken);
283
284
285
286    if (username)
287	CFDictionaryAddValue(info, kNAHUserName, username);
288    if (password)
289	CFDictionaryAddValue(info, kNAHPassword, password);
290
291    na = NAHCreate(NULL, hostname, service, info);
292    if (info)
293	CFRelease(info);
294    if (na == NULL)
295	errx(1, "NAHCreate");
296
297    CFArrayRef r = NAHGetSelections(na);
298
299    CFIndex n, num = CFArrayGetCount(r);
300
301    bool found_wlkdc = false,
302	found_classic_lkdc = false,
303	found_kerberos = false,
304	found_raw_kerberos = false,
305	found_mech_kerberos = false,
306	found_mech_ntlm = false,
307	found_mech_iakerb = false;
308
309    CFShow(hostname);
310
311    for (n = 0; n < num; n++) {
312	NAHSelectionRef s = (NAHSelectionRef)CFArrayGetValueAtIndex(r, n);
313	CFStringRef name;
314	CFRange range1, range2;
315
316	CFShow(s);
317
318	name = NAHSelectionGetInfoForKey(s, kNAHMechanism);
319	if (CFStringCompare(name, CFSTR("Kerberos"), 0) == kCFCompareEqualTo) {
320	    found_raw_kerberos = true;
321	}
322
323	name = NAHSelectionGetInfoForKey(s, kNAHInnerMechanism);
324	if (CFStringCompare(name, CFSTR("Kerberos"), 0) == kCFCompareEqualTo) {
325	    found_mech_kerberos = true;
326
327	    name = NAHSelectionGetInfoForKey(s, kNAHClientPrincipal);
328
329	    range1 = CFStringFind(name, CFSTR("WELLKNOWN:COM.APPLE.LKDC"), 0);
330	    range2 = CFStringFind(name, CFSTR("@LKDC:SHA1"), 0);
331	    if (range1.location != kCFNotFound)
332		found_wlkdc = true;
333	    else if (range2.location != kCFNotFound)
334		found_classic_lkdc = true;
335	    else
336		found_kerberos = true;
337
338	} else if (CFStringCompare(name, CFSTR("IAKerb"), 0) == kCFCompareEqualTo) {
339	    found_mech_iakerb = true;
340	} else if (CFStringCompare(name, CFSTR("NTLM"), 0) == kCFCompareEqualTo) {
341	    found_mech_ntlm = true;
342	}
343    }
344
345    check_valid(expect_mech_kerberos, found_mech_kerberos, "kerberos mech");
346    check_valid(expect_ntlm, found_mech_ntlm, "ntlm mech");
347    check_valid(expect_iakerb, found_mech_iakerb, "iakerb");
348    check_valid(expect_classic_kerberos, found_kerberos, "kerberos classic");
349    check_valid(expect_classic_lkdc, found_classic_lkdc, "lkdc classic mech");
350    check_valid(expect_wlkdc, found_wlkdc, "wellknown lkdc");
351    check_valid(expect_raw_kerberos, found_raw_kerberos, "raw kerberos");
352
353
354    CFRelease(na);
355}
356
357
358uint8_t win2k8[108] =
359    "\x60\x6a\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x60\x30\x5e\xa0\x30"
360    "\x30\x2e\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02\x06\x09\x2a"
361    "\x86\x48\x86\xf7\x12\x01\x02\x02\x06\x0a\x2a\x86\x48\x86\xf7\x12"
362    "\x01\x02\x02\x03\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
363    "\xa3\x2a\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f\x64\x65\x66\x69"
364    "\x6e\x65\x64\x5f\x69\x6e\x5f\x52\x46\x43\x34\x31\x37\x38\x40\x70"
365    "\x6c\x65\x61\x73\x65\x5f\x69\x67\x6e\x6f\x72\x65";
366
367uint8_t snowleopard[] =
368    "\x60\x81\xa6\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x81\x9b\x30\x81"
369    "\x98\xa0\x24\x30\x22\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
370    "\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02\x06\x0a\x2b\x06\x01"
371    "\x04\x01\x82\x37\x02\x02\x0a\xa3\x70\x30\x6e\xa0\x6c\x1b\x6a\x63"
372    "\x69\x66\x73\x2f\x4c\x4b\x44\x43\x3a\x53\x48\x41\x31\x2e\x35\x38"
373    "\x46\x43\x45\x33\x36\x44\x42\x44\x42\x44\x38\x36\x41\x45\x37\x30"
374    "\x31\x30\x39\x33\x42\x34\x42\x31\x44\x37\x39\x41\x44\x44\x30\x37"
375    "\x30\x35\x44\x30\x30\x42\x40\x4c\x4b\x44\x43\x3a\x53\x48\x41\x31"
376    "\x2e\x35\x38\x46\x43\x45\x33\x36\x44\x42\x44\x42\x44\x38\x36\x41"
377    "\x45\x37\x30\x31\x30\x39\x33\x42\x34\x42\x31\x44\x37\x39\x41\x44"
378    "\x44\x30\x37\x30\x35\x44\x30\x30\x42";
379
380uint8_t lion[] =
381    "\x60\x7e\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x74\x30\x72\xa0\x44"
382    "\x30\x42\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02\x06\x09\x2a"
383    "\x86\x48\x86\xf7\x12\x01\x02\x02\x06\x06\x2a\x85\x70\x2b\x0e\x03"
384    "\x06\x06\x2b\x06\x01\x05\x05\x0e\x06\x0a\x2b\x06\x01\x04\x01\x82"
385    "\x37\x02\x02\x0a\x06\x06\x2b\x05\x01\x05\x02\x07\x06\x06\x2b\x06"
386    "\x01\x05\x02\x05\xa3\x2a\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f"
387    "\x64\x65\x66\x69\x6e\x65\x64\x5f\x69\x6e\x5f\x52\x46\x43\x34\x31"
388    "\x37\x38\x40\x70\x6c\x65\x61\x73\x65\x5f\x69\x67\x6e\x6f\x72\x65";
389
390int
391main(int argc, char **argv)
392{
393    /* validate expected behaviors */
394
395    system("/System/Library/PrivateFrameworks/Heimdal.framework/Helpers/gsstool destroy --all");
396
397    /*
398     * Basic tests
399     */
400
401    lkdc_classic();
402    lkdc_wellknown();
403    test_ntlm();
404
405    kerberos_classic(CFSTR("host.ads.apple.com"), CFSTR("host"),
406		     CFSTR("ktestuser"), CFSTR("foobar"));
407
408    kerberos_classic(CFSTR("host.ads.apple.com"), CFSTR("host"),
409		     CFSTR("ktestuser@ADS.APPLE.COM"), CFSTR("foobar"));
410
411    system("/System/Library/PrivateFrameworks/Heimdal.framework/Helpers/gsstool destroy --all");
412
413    /* windows 2008 server */
414    verify_result(true, true, false, true, false, false, false, CFSTR("cifs"), CFSTR("host.ads.apple.com"), CFSTR("user"), CFSTR("password"), win2k8, sizeof(win2k8), NULL);
415    verify_result(false, false, false, false, false, false, false, CFSTR("cifs"), CFSTR("host.ads.apple.com"), NULL, NULL, win2k8, sizeof(win2k8), NULL);
416
417    /* snowleopard SMB */
418    verify_result(true, true, false, true, false, false, false, CFSTR("cifs"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), snowleopard, sizeof(snowleopard), NULL);
419    verify_result(false, false, false, false, false, false, false, CFSTR("cifs"), CFSTR("nutcracker.apple.com"), NULL, NULL, snowleopard, sizeof(snowleopard), NULL);
420    /* next two tests only really for N.N. when his laptop is turned on.... :-/ */
421#if IS_LOVE
422    verify_result(true, true, false, true, true, false, false, CFSTR("cifs"), CFSTR("nutcracker.local"), CFSTR("user"), CFSTR("password"), snowleopard, sizeof(snowleopard), NULL);
423    verify_result(true, true, false, false, true, false, false, CFSTR("cifs"), CFSTR("nutcracker.bitcollector.members.mac.com"), CFSTR("user"), CFSTR("password"), snowleopard, sizeof(snowleopard), NULL);
424#endif
425
426    /* snowleopard VNC */
427    CFShow(CFSTR("VNC - snow leopard"));
428    verify_result(true, false, false, true, false, true, false, CFSTR("vnc"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
429    verify_result(false, false, false, false, false, false, false, CFSTR("vnc"), CFSTR("nutcracker.apple.com"), NULL, NULL, NULL, 0, NULL);
430#if IS_LOVE
431    verify_result(true, false, false, true, true, true, false, CFSTR("vnc"), CFSTR("nutcracker.local"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
432    verify_result(true, false, false, false, true, true, false, CFSTR("vnc"), CFSTR("nutcracker.bitcollector.members.mac.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
433    verify_result(true, false, false, false, true, true, false, CFSTR("vnc"), CFSTR("nutcracker.bitcollector.members.btmm.icloud.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
434#endif
435
436    CFShow(CFSTR("afpserver - No initial packet - snow leopard"));
437    verify_result(true, false, false, true, false, false, true, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
438#if IS_LOVE
439    verify_result(true, false, false, true, true, false, false, CFSTR("afpserver"), CFSTR("nutcracker.local"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
440#endif
441    verify_result(false, false, false, false, false, false, false, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), NULL, NULL, NULL, 0, NULL);
442#if IS_LOVE
443    verify_result(false, false, false, true, true, false, false, CFSTR("afpserver"), CFSTR("nutcracker.local"), NULL, NULL, NULL, 0, NULL);
444#endif
445
446    CFShow(CFSTR("afpserver - Initial packet without IAKERB or SupportLKDC - 3rd party"));
447    verify_result(true, false, false, true, false, false, true, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), snowleopard, sizeof(snowleopard), NULL);
448    verify_result(false, false, false, false, false, false, false, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), NULL, NULL, snowleopard, sizeof(snowleopard), NULL);
449
450    CFShow(CFSTR("afpserver - Initial packet with IAKERB or SupportLKDC - lion"));
451    verify_result(true, false, true, true, false, false, false, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), lion, sizeof(lion), NULL);
452    verify_result(false, false, false, false, false, false, false, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), NULL, NULL, lion, sizeof(lion), NULL);
453#if IS_LOVE
454    verify_result(true, false, false, true, true, true, false, CFSTR("afpserver"), CFSTR("nutcracker.local"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
455    verify_result(true, false, false, false, true, true, false, CFSTR("afpserver"), CFSTR("nutcracker.bitcollector.members.mac.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
456#endif
457
458    printf("PASS\n");
459
460    return 0;
461}
462