//
// Identify a guest by returning its StaticCode and running CodeDirectory hash.
// This uses cshosting RPCs to ask the host (or its proxy).
//
SecStaticCode *GenericCode::identifyGuest(SecCode *guest, CFDataRef *cdhashOut)
{
	if (GenericCode *iguest = dynamic_cast<GenericCode *>(guest)) {
		FilePathOut path;
		CFRef<CFDataRef> cdhash;
		CFDictionary attributes(errSecCSHostProtocolInvalidAttribute);
		identifyGuest(iguest->guestRef(), path, cdhash.aref(), attributes.aref());
		DiskRep::Context ctx;
		if (CFNumberRef architecture = attributes.get<CFNumberRef>(kSecGuestAttributeArchitecture)) {
			cpu_type_t cpu = cfNumber<cpu_type_t>(architecture);
			if (CFNumberRef subarchitecture = attributes.get<CFNumberRef>(kSecGuestAttributeSubarchitecture))
				ctx.arch = Architecture(cpu, cfNumber<cpu_subtype_t>(subarchitecture));
			else
				ctx.arch = Architecture(cpu);
		}
		SecPointer<GenericStaticCode> code = new GenericStaticCode(DiskRep::bestGuess(path, &ctx));
		CODESIGN_GUEST_IDENTIFY_GENERIC(iguest, iguest->guestRef(), code);
		if (cdhash) {
			CODESIGN_GUEST_CDHASH_GENERIC(iguest, (void *)CFDataGetBytePtr(cdhash), (unsigned)CFDataGetLength(cdhash));
			*cdhashOut = cdhash.yield();
		}
		return code.yield();
	} else
		MacOSError::throwMe(errSecCSNotAHost);
}
//
// Given a bag of attribute values, automagically come up with a SecCode
// without any other information.
// This is meant to be the "just do what makes sense" generic call, for callers
// who don't want to engage in the fascinating dance of manual guest enumeration.
//
// Note that we expect the logic embedded here to change over time (in backward
// compatible fashion, one hopes), and that it's all right to use heuristics here
// as long as it's done sensibly.
//
// Be warned that the present logic is quite a bit ad-hoc, and will likely not
// handle arbitrary combinations of proxy hosting, dynamic hosting, and dedicated
// hosting all that well.
//
SecCode *SecCode::autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags)
{
	// special case: with no attributes at all, return the root of trust
	if (CFDictionaryGetCount(attributes) == 0)
		return KernelCode::active()->retain();
	
	// main logic: we need a pid, and we'll take a canonical guest id as an option
	int pid = 0;
	if (!cfscan(attributes, "{%O=%d}", kSecGuestAttributePid, &pid))
		CSError::throwMe(errSecCSUnsupportedGuestAttributes, kSecCFErrorGuestAttributes, attributes);
	if (SecCode *process =
			KernelCode::active()->locateGuest(attributes)) {
		SecPointer<SecCode> code;
		code.take(process);		// locateGuest gave us a retained object
		if (code->staticCode()->flag(kSecCodeSignatureHost)) {
			// might be a code host. Let's find out
			CFRef<CFMutableDictionaryRef> rest = makeCFMutableDictionary(attributes);
			CFDictionaryRemoveValue(rest, kSecGuestAttributePid);
			if (SecCode *guest = code->locateGuest(rest))
				return guest;
		}
		if (!CFDictionaryGetValue(attributes, kSecGuestAttributeCanonical)) {
			// only "soft" attributes, and no hosting is happening. Return the (non-)host itself
			return code.yield();
		}
	}
	MacOSError::throwMe(errSecCSNoSuchCode);
}
//
// Identify a guest by attribute set, and return a new GenericCode representing it.
// This uses cshosting RPCs to ask the host (or its proxy).
//
SecCode *GenericCode::locateGuest(CFDictionaryRef attributes)
{
	if (Port host = hostingPort()) {
		CFRef<CFDataRef> attrData;
		void *attrPtr = NULL; size_t attrLength = 0;
		if (attributes) {
			attrData.take(CFPropertyListCreateXMLData(NULL, attributes));
			attrPtr = (void *)CFDataGetBytePtr(attrData);
			attrLength = CFDataGetLength(attrData);
		}
		GuestChain guestPath;
		mach_msg_type_number_t guestPathLength;
		mach_port_t subport;
		CALL(host, findGuest, guestRef(), attrPtr, (mach_msg_type_number_t)attrLength,
			&guestPath, &guestPathLength, &subport);
		CODESIGN_GUEST_LOCATE_GENERIC(this, guestPath, guestPathLength, subport);
		SecPointer<SecCode> code = this;
		for (unsigned n = 0; n < guestPathLength; n++)
			code = new GenericCode(code, guestPath[n]);
		return code.yield();
	} else
		return NULL;		// not found, no error
}