#include "AppleDotMacTPSession.h"
#include "dotMacTpDebug.h"
#include "dotMacTpUtils.h"
#include "dotMacTpMutils.h"
#include "dotMacTp.h"
#include "dotMacTpAsn1Templates.h"
#include <security_asn1/SecNssCoder.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <security_cdsa_utils/cuPem.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
#define CFRELEASE(cf) if(cf != NULL) { CFRelease(cf); }
void dotMacTpbuildX509Name(
SecNssCoder &coder,
uint32 numTypeValuePairs, CSSM_X509_TYPE_VALUE_PAIR_PTR typeValuePairs,
CSSM_X509_NAME &x509Name)
{
memset(&x509Name, 0, sizeof(x509Name));
x509Name.RelativeDistinguishedName =
coder.mallocn<CSSM_X509_RDN>(numTypeValuePairs);
for(unsigned nameDex=0; nameDex<numTypeValuePairs; nameDex++) {
CSSM_X509_RDN_PTR rdn = &x509Name.RelativeDistinguishedName[nameDex];
rdn->numberOfPairs = 1;
rdn->AttributeTypeAndValue = &typeValuePairs[nameDex];
}
x509Name.numberOfRDNs = numTypeValuePairs;
}
void dotMacRefKeyToRaw(
CSSM_CSP_HANDLE cspHand,
const CSSM_KEY *refKey,
CSSM_KEY_PTR rawKey) {
CSSM_CC_HANDLE ccHand;
CSSM_RETURN crtn;
CSSM_ACCESS_CREDENTIALS creds;
memset(rawKey, 0, sizeof(CSSM_KEY));
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
CSSM_ALGID_NONE,
CSSM_ALGMODE_NONE,
&creds, NULL, NULL, CSSM_PADDING_NONE, 0, &ccHand);
if(crtn) {
dotMacErrorLog("dotMacRefKeyToRaw: context err");
CssmError::throwMe(crtn);
}
crtn = CSSM_WrapKey(ccHand,
&creds,
refKey,
NULL, rawKey);
if(crtn != CSSM_OK) {
dotMacErrorLog("dotMacRefKeyToRaw: wrapKey err");
CssmError::throwMe(crtn);
}
CSSM_DeleteContext(ccHand);
}
void dotMacTokenizeHostName(
const CSSM_DATA &inName, CSSM_DATA &outName, CSSM_DATA &outDomain) {
int idx = 0;
int stopIdx = inName.Length;
uint8 *p = inName.Data;
outName = inName;
outDomain.Length = idx;
if (!p) return;
while (idx < stopIdx) {
if (*p++ == '.') {
outName.Length = idx;
outDomain.Length = (CSSM_SIZE)(stopIdx - (idx + 1));
outDomain.Data = p;
break;
}
idx++;
}
if (outDomain.Length) {
idx = 0;
stopIdx = outDomain.Length;
while (idx < stopIdx) {
if (*p++ == ':') {
outDomain.Length = idx;
break;
}
idx++;
}
}
}
void dotMacTokenizeUserName(
const CSSM_DATA &inName, CSSM_DATA &outName, CSSM_DATA &outDomain) {
int idx = 0;
int stopIdx = inName.Length;
uint8 *p = inName.Data;
outName = inName;
outDomain.Length = idx;
if (!p) return;
while (idx < stopIdx) {
if (*p++ == '@') {
outName.Length = idx;
outDomain.Length = (CSSM_SIZE)(stopIdx - (idx + 1));
outDomain.Data = p;
break;
}
idx++;
}
}
OSStatus dotMacEncodeRefId(
const CSSM_DATA &userName, const CSSM_DATA &domainName, DotMacCertTypeTag certTypeTag,
SecNssCoder &coder, CSSM_DATA &refId) {
DotMacTpPendingRequest req;
req.userName = userName;
req.domainName = domainName;
uint8 certType = certTypeTag;
req.certTypeTag.Data = &certType;
req.certTypeTag.Length = 1;
CSSM_DATA tempData = {0, NULL};
PRErrorCode prtn = coder.encodeItem(&req, DotMacTpPendingRequestTemplate, tempData);
if(prtn) {
dotMacErrorLog("dotMacEncodeRefId: encodeItem error");
return internalComponentErr;
}
unsigned char *pem;
unsigned pemLen;
if(pemEncode(tempData.Data, tempData.Length, &pem, &pemLen, "REFERENCE ID")) {
dotMacErrorLog("dotMacEncodeRefId: pemEncode error");
return internalComponentErr;
}
refId.Data = NULL;
refId.Length = 0;
coder.allocCopyItem(pem, pemLen, refId);
free(pem);
return noErr;
}
OSStatus dotMacDecodeRefId(
SecNssCoder &coder, const CSSM_DATA &refId, CSSM_DATA &userName, CSSM_DATA &domainName, DotMacCertTypeTag *certTypeTag) {
unsigned char *unPem;
unsigned unPemLen;
if(pemDecode(refId.Data, refId.Length, &unPem, &unPemLen)) {
dotMacErrorLog("dotMacDecodeRefId: pemDecode error");
return internalComponentErr;
}
CSSM_DATA tempData;
tempData.Data = unPem;
tempData.Length = unPemLen;
DotMacTpPendingRequest req;
memset(&req, 0, sizeof(req));
PRErrorCode prtn = coder.decodeItem(tempData, DotMacTpPendingRequestTemplate, &req);
free(unPem);
if(prtn) {
dotMacErrorLog("dotMacDecodeRefId: decodeItem error");
return paramErr;
}
userName = req.userName;
domainName = req.domainName;
if(req.certTypeTag.Length != 1) {
dotMacErrorLog("dotMacDecodeRefId: reqType length (%lu) error", req.certTypeTag.Length);
return paramErr;
}
*certTypeTag = req.certTypeTag.Data[0];
return noErr;
}
#define _kCFStreamPropertyReadTimeout CFSTR("_kCFStreamPropertyReadTimeout")
#define READ_STREAM_TIMEOUT 15
#define READ_FRAGMENT_SIZE 512
CSSM_RETURN dotMacTpCertFetch(
const CSSM_DATA &userName, const CSSM_DATA &domainName, DotMacCertTypeTag certType,
Allocator &alloc, CSSM_DATA &result) {
unsigned rawUrlLen;
unsigned domainLen = (domainName.Length && domainName.Data) ? domainName.Length : strlen(DOT_MAC_DOMAIN);
uint8 *domain = (domainName.Length && domainName.Data) ? domainName.Data : (uint8 *) DOT_MAC_DOMAIN;
const char *typeArg;
CSSM_RETURN crtn = CSSM_OK;
switch(certType) {
case CSSM_DOT_MAC_TYPE_ICHAT:
case CSSM_DOT_MAC_TYPE_UNSPECIFIED:
typeArg = DOT_MAC_CERT_TYPE_ICHAT;
break;
case CSSM_DOT_MAC_TYPE_SHARED_SERVICES:
typeArg = DOT_MAC_CERT_TYPE_SHARED_SERVICES;
break;
case CSSM_DOT_MAC_TYPE_EMAIL_SIGNING:
typeArg = DOT_MAC_CERT_TYPE_EMAIL_SIGNING;
break;
case CSSM_DOT_MAC_TYPE_EMAIL_ENCRYPT:
typeArg = DOT_MAC_CERT_TYPE_EMAIL_ENCRYPT;
break;
default:
dotMacErrorLog("dotMacTpCertFetch: bad signType");
return paramErr;
}
rawUrlLen = strlen(DOT_MAC_LOOKUP_SCHEMA) +
strlen(DOT_MAC_LOOKUP_HOST_NAME) +
domainLen + 1 +
strlen(DOT_MAC_LOOKUP_PATH) +
userName.Length +
strlen(DOT_MAC_LOOKUP_TYPE) +
strlen(typeArg) +
1;
unsigned char rawUrl[rawUrlLen];
unsigned char *cp = rawUrl;
unsigned len = strlen(DOT_MAC_LOOKUP_SCHEMA);
memmove(cp, DOT_MAC_LOOKUP_SCHEMA, len);
cp += len;
len = strlen(DOT_MAC_LOOKUP_HOST_NAME);
memmove(cp, DOT_MAC_LOOKUP_HOST_NAME, len);
cp += len;
memmove(cp, ".", 1);
cp += 1;
memmove(cp, domain, domainLen);
cp += domainLen;
len = strlen(DOT_MAC_LOOKUP_PATH);
memmove(cp, DOT_MAC_LOOKUP_PATH, len);
cp += len;
memmove(cp, userName.Data, userName.Length);
cp += userName.Length;
len = strlen(DOT_MAC_LOOKUP_TYPE);
memmove(cp, DOT_MAC_LOOKUP_TYPE, len);
cp += len;
len = strlen(typeArg);
memmove(cp, typeArg, len);
cp += len;
*cp = '\0'; dotMacDebug("dotMacTpCertFetch: URL %s", rawUrl);
CFURLRef cfUrl = CFURLCreateWithBytes(NULL,
rawUrl, rawUrlLen - 1, kCFStringEncodingUTF8,
NULL); if(cfUrl == NULL) {
dotMacErrorLog("dotMacTpCertFetch: CFURLCreateWithBytes returned NULL\n");
return paramErr;
}
CFHTTPMessageRef httpRequestRef = NULL;
CFReadStreamRef httpStreamRef = NULL;
CFNumberRef cfnTo = NULL;
CFDictionaryRef proxyDict = NULL;
SInt32 ito = READ_STREAM_TIMEOUT;
CFMutableDataRef fetchedData = CFDataCreateMutable(NULL, 0);
UInt8 readFrag[READ_FRAGMENT_SIZE];
CFIndex bytesRead;
CFIndex resultLen;
httpRequestRef = CFHTTPMessageCreateRequest(NULL,
CFSTR("GET"), cfUrl, kCFHTTPVersion1_1);
if(!httpRequestRef) {
dotMacErrorLog("***Error creating HTTPMessage from '%s'\n", rawUrl);
crtn = ioErr;
goto errOut;
}
httpStreamRef = CFReadStreamCreateForHTTPRequest(NULL, httpRequestRef);
if(!httpStreamRef) {
dotMacErrorLog("***Error creating stream for '%s'\n", rawUrl);
crtn = ioErr;
goto errOut;
}
cfnTo = CFNumberCreate(NULL, kCFNumberSInt32Type, &ito);
if(!CFReadStreamSetProperty(httpStreamRef, _kCFStreamPropertyReadTimeout, cfnTo)) {
}
proxyDict = SCDynamicStoreCopyProxies(NULL);
if(proxyDict) {
CFReadStreamSetProperty(httpStreamRef, kCFStreamPropertyHTTPProxy, proxyDict);
}
if(CFReadStreamOpen(httpStreamRef) == false) {
dotMacErrorLog("***Error opening stream for '%s'\n", rawUrl);
crtn = ioErr;
goto errOut;
}
bytesRead = CFReadStreamRead(httpStreamRef, readFrag, sizeof(readFrag));
while (bytesRead > 0) {
CFDataAppendBytes(fetchedData, readFrag, bytesRead);
bytesRead = CFReadStreamRead(httpStreamRef, readFrag, sizeof(readFrag));
}
if (bytesRead < 0) {
dotMacErrorLog("***Error reading URL '%s'\n", rawUrl);
crtn = ioErr;
goto errOut;
}
resultLen = CFDataGetLength(fetchedData);
if(resultLen == 0) {
dotMacErrorLog("***No data available from URL '%s'\n", rawUrl);
goto errOut;
}
{
bool isPEM = false;
const char *srchStr = "-----BEGIN CERTIFICATE-----";
unsigned srchStrLen = strlen(srchStr);
const char *p = (const char *)CFDataGetBytePtr(fetchedData);
if(resultLen > (int)srchStrLen) {
unsigned srchLen = resultLen - srchStrLen;
for(unsigned dex=0; dex< srchLen; dex++) {
if(!strncmp(p, srchStr, srchStrLen)) {
isPEM = true;
break;
}
p++;
}
}
if(isPEM) {
result.Data = (uint8 *)alloc.malloc(resultLen);
result.Length = resultLen;
memmove(result.Data, CFDataGetBytePtr(fetchedData), resultLen);
}
else {
result.Data = NULL;
result.Length = 0;
}
}
errOut:
CFRELEASE(cfUrl);
CFRELEASE(httpRequestRef);
CFRELEASE(httpStreamRef);
CFRELEASE(cfnTo);
CFRELEASE(proxyDict);
return crtn;
}