test-na.c   [plain text]


/*
 * Copyright (c) 2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#include "NetworkAuthenticationHelper.h"
#include "KerberosHelper.h"

#include <err.h>

#define IS_LOVE 0

static void
lkdc_classic(void)
{
    CFArrayRef array;
    CFIndex n;
    NAHRef na;

    CFShow(CFSTR("lkdc_classic"));

    na = NAHCreate(NULL, CFSTR("localhost.local"), CFSTR("host"), NULL);
    if (na == NULL)
	errx(1, "NACreate");

    array = NAHGetSelections(na);

    for (n = 0; n < CFArrayGetCount(array); n++)
	CFShow(CFArrayGetValueAtIndex(array, n));

    CFRelease(na);
}

uint8_t token[] =
    "\x60\x66\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x5c"
    "\x30\x5a\xa0\x2c\x30\x2a\x06\x09\x2a\x86\x48\x82"
    "\xf7\x12\x01\x02\x02\x06\x09\x2a\x86\x48\x86\xf7"
    "\x12\x01\x02\x02\x06\x0a\x2b\x06\x01\x04\x01\x82"
    "\x37\x02\x02\x0a\x06\x06\x2b\x05\x01\x05\x02\x07"
    "\xa3\x2a\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f"
    "\x64\x65\x66\x69\x6e\x65\x64\x5f\x69\x6e\x5f\x52"
    "\x46\x43\x34\x31\x37\x38\x40\x70\x6c\x65\x61\x73"
    "\x65\x5f\x69\x67\x6e\x6f\x72\x65";

static void
lkdc_wellknown(void)
{
    CFMutableDictionaryRef info;
    CFDictionaryRef negToken;
    CFArrayRef array;
    CFDataRef serverToken;
    CFIndex n;
    NAHRef na;

    CFShow(CFSTR("lkdc_wellknown"));

    serverToken = CFDataCreateWithBytesNoCopy(NULL, 
					      token, sizeof(token) - 1,
					      kCFAllocatorNull);


    negToken = KRBDecodeNegTokenInit(NULL, serverToken);
    CFRelease(serverToken);

    info = CFDictionaryCreateMutable(NULL,
				     0,
				     &kCFTypeDictionaryKeyCallBacks,
				     &kCFTypeDictionaryValueCallBacks);

    CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
    CFRelease(negToken);


    na = NAHCreate(NULL, CFSTR("localhost.local"), kNAHServiceAFPServer, info);
    CFRelease(info);
    if (na == NULL)
	errx(1, "NACreate");

    array = NAHGetSelections(na);

    for (n = 0; n < CFArrayGetCount(array); n++)
	CFShow(CFArrayGetValueAtIndex(array, n));

    CFRelease(na);
}

static void
test_ntlm(void)
{
    CFMutableDictionaryRef info;
    CFDictionaryRef negToken;
    CFArrayRef array;
    CFIndex n;
    NAHRef na;

    CFShow(CFSTR("test_ntlm"));

    negToken = KRBCreateNegTokenLegacyNTLM(NULL);
    if (negToken == NULL)
	errx(1, "KRBCreateNegTokenLegacyNTLM");

    info = CFDictionaryCreateMutable(NULL,
				     0,
				     &kCFTypeDictionaryKeyCallBacks,
				     &kCFTypeDictionaryValueCallBacks);

    CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
    CFRelease(negToken);


    na = NAHCreate(NULL, CFSTR("localhost.local"), CFSTR("host"), info);
    CFRelease(info);
    if (na == NULL)
	errx(1, "NACreate");

    array = NAHGetSelections(na);

    for (n = 0; n < CFArrayGetCount(array); n++) {
	NAHSelectionRef s = (void *)CFArrayGetValueAtIndex(array, n);
	CFShow(s);

	if (CFBooleanGetValue(NAHSelectionGetInfoForKey(s, kNAHUseSPNEGO)))
	    errx(1, "not raw NTLM");
    }

    CFRelease(na);
}

static void
kerberos_classic(CFStringRef hostname,
		 CFStringRef service,
		 CFStringRef user,
		 CFStringRef password)
{
    CFMutableDictionaryRef info;
    CFDictionaryRef negToken;
    CFArrayRef array;
    CFIndex n;
    NAHRef na;

    CFShow(CFSTR("test KRBCreateNegTokenLegacyKerberos"));

    negToken = KRBCreateNegTokenLegacyKerberos(NULL);
    if (negToken == NULL)
	errx(1, "KRBCreateNegTokenLegacyKerberos");

    CFShow(CFSTR("kerberos_classic"));

    info = CFDictionaryCreateMutable(NULL,
				     0,
				     &kCFTypeDictionaryKeyCallBacks,
				     &kCFTypeDictionaryValueCallBacks);

    CFDictionaryAddValue(info, kNAHUserName, user);
    CFDictionaryAddValue(info, kNAHPassword, password);

    CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
    CFRelease(negToken);


    na = NAHCreate(NULL, hostname, service, info);
    if (na == NULL)
	errx(1, "NAHCreate");

    array = NAHGetSelections(na);

    for (n = 0; n < CFArrayGetCount(array); n++) {
	NAHSelectionRef s = (void *)CFArrayGetValueAtIndex(array, n);
	CFErrorRef error = NULL;
	Boolean ret;

	ret = NAHSelectionAcquireCredential(s, NULL, &error);
	printf("NAHSelectionAcquireCredential: %s\n", ret ? "yes" : "no");
	CFShow(s);
	if (error)
	    CFShow(error);

	if (ret) {
	    CFDictionaryRef authinfo = NAHSelectionCopyAuthInfo(s);
	    if (authinfo) {
		CFShow(CFSTR("Authinfo"));
		CFShow(authinfo);
		CFRelease(authinfo);
	    }

	    /* if NAHCopyReferenceKey returns NULL, refcounting not supported */

	    CFStringRef refinfo = NAHCopyReferenceKey(s);
		
	    CFStringRef label = CFSTR("fs:/testing");

	    NAHAddReferenceAndLabel(s, label);
	    
	    NAHCredRemoveReference(refinfo);
		
	    NAHCredAddReference(refinfo);
	    NAHCredRemoveReference(refinfo);
	    
	    if (refinfo)
		CFRelease(refinfo);

	    /* should delete */
	    NAHFindByLabelAndRelease(label);
	}
    }

    CFRelease(na);

    CFRelease(info);
}

/*
 *
 */

static void
check_valid(bool expected, bool found, const char *check)
{
    if (expected ^ found)
	errx(1, "%s doesn't have expected result: %s %sfound",
	     check, check, found ? "" : "not ");
}


static void
verify_result(bool expect_mech_kerberos,
	      bool expect_ntlm,
	      bool expect_iakerb,
	      bool expect_classic_kerberos,
	      bool expect_classic_lkdc,
	      bool expect_wlkdc,
	      bool expect_raw_kerberos, 
	      CFStringRef service,
	      CFStringRef hostname,
	      CFStringRef username,
	      CFStringRef password,
	      void *spnego,
	      size_t len,
	      CFDictionaryRef negToken)
{
    CFDataRef serverToken = CFDataCreate(NULL, spnego, len);
    CFMutableDictionaryRef info;
    NAHRef na;
    
    info = CFDictionaryCreateMutable(NULL,
				     0,
				     &kCFTypeDictionaryKeyCallBacks,
				     &kCFTypeDictionaryValueCallBacks);
    
    if (spnego) {
	negToken = KRBDecodeNegTokenInit(NULL, serverToken);
	CFRelease(serverToken);
	if (negToken == NULL)
	    errx(1, "failed to decode NegTokenInit");
	
	CFShow(negToken);

	CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
	CFRelease(negToken);
    } else if (negToken) {
	CFShow(negToken);
	CFDictionaryAddValue(info, kNAHNegTokenInit, negToken);
    }
    CFRelease(serverToken);

    
    
    if (username)
	CFDictionaryAddValue(info, kNAHUserName, username);
    if (password)
	CFDictionaryAddValue(info, kNAHPassword, password);
    
    na = NAHCreate(NULL, hostname, service, info);
    if (info)
	CFRelease(info);
    if (na == NULL)
	errx(1, "NAHCreate");

    CFArrayRef r = NAHGetSelections(na);

    CFIndex n, num = CFArrayGetCount(r);

    bool found_wlkdc = false,
	found_classic_lkdc = false,
	found_kerberos = false,
	found_raw_kerberos = false,
	found_mech_kerberos = false,
	found_mech_ntlm = false,
	found_mech_iakerb = false;

    CFShow(hostname);

    for (n = 0; n < num; n++) {
	NAHSelectionRef s = (NAHSelectionRef)CFArrayGetValueAtIndex(r, n);
	CFStringRef name;
	CFRange range1, range2;
	
	CFShow(s);
	
	name = NAHSelectionGetInfoForKey(s, kNAHMechanism);
	if (CFStringCompare(name, CFSTR("Kerberos"), 0) == kCFCompareEqualTo) {
	    found_raw_kerberos = true;
	}

	name = NAHSelectionGetInfoForKey(s, kNAHInnerMechanism);
	if (CFStringCompare(name, CFSTR("Kerberos"), 0) == kCFCompareEqualTo) {
	    found_mech_kerberos = true;
	    
	    name = NAHSelectionGetInfoForKey(s, kNAHClientPrincipal);
	    
	    range1 = CFStringFind(name, CFSTR("WELLKNOWN:COM.APPLE.LKDC"), 0);
	    range2 = CFStringFind(name, CFSTR("@LKDC:SHA1"), 0);
	    if (range1.location != kCFNotFound)
		found_wlkdc = true;
	    else if (range2.location != kCFNotFound)
		found_classic_lkdc = true;
	    else
		found_kerberos = true;
	    
	} else if (CFStringCompare(name, CFSTR("IAKerb"), 0) == kCFCompareEqualTo) {
	    found_mech_iakerb = true;
	} else if (CFStringCompare(name, CFSTR("NTLM"), 0) == kCFCompareEqualTo) {
	    found_mech_ntlm = true;
	}
    }
    
    check_valid(expect_mech_kerberos, found_mech_kerberos, "kerberos mech");
    check_valid(expect_ntlm, found_mech_ntlm, "ntlm mech");
    check_valid(expect_iakerb, found_mech_iakerb, "iakerb");
    check_valid(expect_classic_kerberos, found_kerberos, "kerberos classic");
    check_valid(expect_classic_lkdc, found_classic_lkdc, "lkdc classic mech");
    check_valid(expect_wlkdc, found_wlkdc, "wellknown lkdc");
    check_valid(expect_raw_kerberos, found_raw_kerberos, "raw kerberos");

    
    CFRelease(na);
}


uint8_t win2k8[108] = 
    "\x60\x6a\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x60\x30\x5e\xa0\x30"
    "\x30\x2e\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02\x06\x09\x2a"
    "\x86\x48\x86\xf7\x12\x01\x02\x02\x06\x0a\x2a\x86\x48\x86\xf7\x12"
    "\x01\x02\x02\x03\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
    "\xa3\x2a\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f\x64\x65\x66\x69"
    "\x6e\x65\x64\x5f\x69\x6e\x5f\x52\x46\x43\x34\x31\x37\x38\x40\x70"
    "\x6c\x65\x61\x73\x65\x5f\x69\x67\x6e\x6f\x72\x65";

uint8_t snowleopard[] = 
    "\x60\x81\xa6\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x81\x9b\x30\x81"
    "\x98\xa0\x24\x30\x22\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
    "\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02\x06\x0a\x2b\x06\x01"
    "\x04\x01\x82\x37\x02\x02\x0a\xa3\x70\x30\x6e\xa0\x6c\x1b\x6a\x63"
    "\x69\x66\x73\x2f\x4c\x4b\x44\x43\x3a\x53\x48\x41\x31\x2e\x35\x38"
    "\x46\x43\x45\x33\x36\x44\x42\x44\x42\x44\x38\x36\x41\x45\x37\x30"
    "\x31\x30\x39\x33\x42\x34\x42\x31\x44\x37\x39\x41\x44\x44\x30\x37"
    "\x30\x35\x44\x30\x30\x42\x40\x4c\x4b\x44\x43\x3a\x53\x48\x41\x31"
    "\x2e\x35\x38\x46\x43\x45\x33\x36\x44\x42\x44\x42\x44\x38\x36\x41"
    "\x45\x37\x30\x31\x30\x39\x33\x42\x34\x42\x31\x44\x37\x39\x41\x44"
    "\x44\x30\x37\x30\x35\x44\x30\x30\x42";

uint8_t lion[] =
    "\x60\x7e\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x74\x30\x72\xa0\x44"
    "\x30\x42\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02\x06\x09\x2a"
    "\x86\x48\x86\xf7\x12\x01\x02\x02\x06\x06\x2a\x85\x70\x2b\x0e\x03"
    "\x06\x06\x2b\x06\x01\x05\x05\x0e\x06\x0a\x2b\x06\x01\x04\x01\x82"
    "\x37\x02\x02\x0a\x06\x06\x2b\x05\x01\x05\x02\x07\x06\x06\x2b\x06"
    "\x01\x05\x02\x05\xa3\x2a\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f"
    "\x64\x65\x66\x69\x6e\x65\x64\x5f\x69\x6e\x5f\x52\x46\x43\x34\x31"
    "\x37\x38\x40\x70\x6c\x65\x61\x73\x65\x5f\x69\x67\x6e\x6f\x72\x65";

int
main(int argc, char **argv)
{
    /* validate expected behaviors */
    
    system("/System/Library/PrivateFrameworks/Heimdal.framework/Helpers/gsstool destroy --all");

    /*
     * Basic tests
     */
    
    lkdc_classic();
    lkdc_wellknown();
    test_ntlm();

    kerberos_classic(CFSTR("host.ads.apple.com"), CFSTR("host"),
		     CFSTR("ktestuser"), CFSTR("foobar"));

    kerberos_classic(CFSTR("host.ads.apple.com"), CFSTR("host"),
		     CFSTR("ktestuser@ADS.APPLE.COM"), CFSTR("foobar"));

    system("/System/Library/PrivateFrameworks/Heimdal.framework/Helpers/gsstool destroy --all");

    /* windows 2008 server */
    verify_result(true, true, false, true, false, false, false, CFSTR("cifs"), CFSTR("host.ads.apple.com"), CFSTR("user"), CFSTR("password"), win2k8, sizeof(win2k8), NULL);
    verify_result(false, false, false, false, false, false, false, CFSTR("cifs"), CFSTR("host.ads.apple.com"), NULL, NULL, win2k8, sizeof(win2k8), NULL);
    
    /* snowleopard SMB */
    verify_result(true, true, false, true, false, false, false, CFSTR("cifs"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), snowleopard, sizeof(snowleopard), NULL);
    verify_result(false, false, false, false, false, false, false, CFSTR("cifs"), CFSTR("nutcracker.apple.com"), NULL, NULL, snowleopard, sizeof(snowleopard), NULL);
    /* next two tests only really for N.N. when his laptop is turned on.... :-/ */
#if IS_LOVE
    verify_result(true, true, false, true, true, false, false, CFSTR("cifs"), CFSTR("nutcracker.local"), CFSTR("user"), CFSTR("password"), snowleopard, sizeof(snowleopard), NULL);
    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);
#endif

    /* snowleopard VNC */
    CFShow(CFSTR("VNC - snow leopard"));
    verify_result(true, false, false, true, false, true, false, CFSTR("vnc"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
    verify_result(false, false, false, false, false, false, false, CFSTR("vnc"), CFSTR("nutcracker.apple.com"), NULL, NULL, NULL, 0, NULL);
#if IS_LOVE
    verify_result(true, false, false, true, true, true, false, CFSTR("vnc"), CFSTR("nutcracker.local"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
    verify_result(true, false, false, false, true, true, false, CFSTR("vnc"), CFSTR("nutcracker.bitcollector.members.mac.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
    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);
#endif

    CFShow(CFSTR("afpserver - No initial packet - snow leopard"));
    verify_result(true, false, false, true, false, false, true, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
#if IS_LOVE
    verify_result(true, false, false, true, true, false, false, CFSTR("afpserver"), CFSTR("nutcracker.local"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
#endif
    verify_result(false, false, false, false, false, false, false, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), NULL, NULL, NULL, 0, NULL);
#if IS_LOVE
    verify_result(false, false, false, true, true, false, false, CFSTR("afpserver"), CFSTR("nutcracker.local"), NULL, NULL, NULL, 0, NULL);
#endif

    CFShow(CFSTR("afpserver - Initial packet without IAKERB or SupportLKDC - 3rd party"));
    verify_result(true, false, false, true, false, false, true, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), snowleopard, sizeof(snowleopard), NULL);
    verify_result(false, false, false, false, false, false, false, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), NULL, NULL, snowleopard, sizeof(snowleopard), NULL);

    CFShow(CFSTR("afpserver - Initial packet with IAKERB or SupportLKDC - lion"));
    verify_result(true, false, true, true, false, false, false, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), CFSTR("user"), CFSTR("password"), lion, sizeof(lion), NULL);
    verify_result(false, false, false, false, false, false, false, CFSTR("afpserver"), CFSTR("nutcracker.apple.com"), NULL, NULL, lion, sizeof(lion), NULL);
#if IS_LOVE
    verify_result(true, false, false, true, true, true, false, CFSTR("afpserver"), CFSTR("nutcracker.local"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
    verify_result(true, false, false, false, true, true, false, CFSTR("afpserver"), CFSTR("nutcracker.bitcollector.members.mac.com"), CFSTR("user"), CFSTR("password"), NULL, 0, NULL);
#endif

    printf("PASS\n");
    
    return 0;
}