1/*
2 * Copyright (c) 2000,2002,2005-2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23/*
24 * ocspdNetwork.cpp - Network support for ocspd and CRL/cert fetch
25 */
26
27#include <security_ocspd/ocspdDebug.h>
28#include "ocspNetwork.h"
29#include <security_ocspd/ocspdUtils.h>
30#include <CoreFoundation/CoreFoundation.h>
31#include <CoreServices/CoreServices.h>
32#include <security_cdsa_utils/cuEnc64.h>
33#include <stdlib.h>
34#include <Security/cssmapple.h>
35#include <LDAP/ldap.h>
36
37#pragma mark ----- OCSP support -----
38
39/* POST method has Content-Type header line equal to "application/ocsp-request" */
40static CFStringRef kContentType		= CFSTR("Content-Type");
41static CFStringRef kAppOcspRequest	= CFSTR("application/ocsp-request");
42
43#ifndef	NDEBUG
44#define DUMP_BLOBS	0
45#else
46#define DUMP_BLOBS	0
47#endif
48
49#define OCSP_GET_FILE	"/tmp/ocspGet"
50#define OCSP_RESP_FILE	"/tmp/ocspResp"
51
52#if		DUMP_BLOBS
53
54#include <security_cdsa_utils/cuFileIo.h>
55
56static void writeBlob(
57	const char *fileName,
58	const char *whatIsIt,
59	const unsigned char *data,
60	unsigned dataLen)
61{
62	if(writeFile(fileName, data, dataLen)) {
63		printf("***Error writing %s to %s\n", whatIsIt, fileName);
64	}
65	else {
66		printf("...wrote %u bytes of %s to %s\n", dataLen, whatIsIt, fileName);
67	}
68}
69
70#else
71
72#define writeBlob(f,w,d,l)
73
74#endif	/* DUMP_BLOBS */
75
76/* fetch via HTTP POST */
77
78#define POST_BUFSIZE	1024
79
80CSSM_RETURN ocspdHttpPost(
81	SecAsn1CoderRef		coder,
82	const CSSM_DATA 	&url,
83	const CSSM_DATA		&ocspReq,	// DER encoded
84	CSSM_DATA			&fetched)	// mallocd in coder space and RETURNED
85{
86	CSSM_RETURN ourRtn = CSSM_OK;
87	CFIndex thisMove;
88	UInt8 inBuf[POST_BUFSIZE];
89	/* resources to release on exit */
90	CFMutableDataRef inData = NULL;
91	CFReadStreamRef cfStream = NULL;
92    CFHTTPMessageRef request = NULL;
93	CFDataRef postData = NULL;
94	CFURLRef cfUrl = NULL;
95
96	/* trim off possible NULL terminator from incoming URL */
97	uint32 urlLen = url.Length;
98	if(url.Data[urlLen - 1] == '\0') {
99		urlLen--;
100	}
101
102	cfUrl = CFURLCreateWithBytes(NULL,
103		url.Data, urlLen,
104		kCFStringEncodingUTF8,		// right?
105		NULL);						// this is absolute path
106	if(cfUrl == NULL) {
107		ocspdErrorLog("CFURLCreateWithBytes returned NULL\n");
108		/* FIXME..? */
109		return CSSMERR_APPLETP_CRL_BAD_URI;
110	}
111	/* subsequent errors to errOut: */
112
113	#ifndef	NDEBUG
114	{
115		char *ustr = (char *)malloc(urlLen + 1);
116		memmove(ustr, url.Data, urlLen);
117		ustr[urlLen] = '\0';
118		ocspdDebug("ocspdHttpPost: posting to URI %s", ustr);
119		free(ustr);
120	}
121	#endif
122
123	writeBlob(OCSP_GET_FILE, "OCSP Request as POST data", ocspReq.Data, ocspReq.Length);
124	postData = CFDataCreate(NULL, ocspReq.Data, ocspReq.Length);
125
126    /* Create a new HTTP request. */
127    request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("POST"), cfUrl,
128		kCFHTTPVersion1_1);
129    if (request == NULL) {
130		ocspdErrorLog("ocspdHttpPost: error creating CFHTTPMessage\n");
131		ourRtn = CSSMERR_TP_INTERNAL_ERROR;
132		goto errOut;
133    }
134
135	// Set the body and required header fields.
136	CFHTTPMessageSetBody(request, postData);
137	CFHTTPMessageSetHeaderFieldValue(request, kContentType, kAppOcspRequest);
138
139    // Create the stream for the request.
140    cfStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
141    if (cfStream == NULL) {
142		ocspdErrorLog("ocspdHttpPost: error creating CFReadStream\n");
143		ourRtn = CSSMERR_TP_INTERNAL_ERROR;
144		goto errOut;
145    }
146
147	/* go, synchronously */
148	if(!CFReadStreamOpen(cfStream)) {
149		ocspdErrorLog("ocspdHttpPost: error opening CFReadStream\n");
150		ourRtn = CSSMERR_TP_INTERNAL_ERROR;
151		goto errOut;
152	}
153	inData = CFDataCreateMutable(NULL, 0);
154	for(;;) {
155		thisMove = CFReadStreamRead(cfStream, inBuf, POST_BUFSIZE);
156		if(thisMove < 0) {
157			CFStreamError error = CFReadStreamGetError(cfStream);
158			ocspdErrorLog("ocspdHttpPost: error on CFReadStreamRead: domain "
159				"%ld error %ld\n", (long)error.domain, (long)error.error);
160			ourRtn = CSSMERR_APPLETP_NETWORK_FAILURE;
161			break;
162		}
163		else if(thisMove == 0) {
164			ocspdDebug("ocspdHttpPost: transfer complete, moved %ld bytes",
165				CFDataGetLength(inData));
166			ourRtn = CSSM_OK;
167			break;
168		}
169		else {
170			CFDataAppendBytes(inData, inBuf, thisMove);
171		}
172	}
173	if(ourRtn == CSSM_OK) {
174		SecAsn1AllocCopy(coder, CFDataGetBytePtr(inData), CFDataGetLength(inData),
175			&fetched);
176		writeBlob(OCSP_RESP_FILE, "OCSP Response", fetched.Data, fetched.Length);
177	}
178
179errOut:
180	CFRELEASE(inData);
181	CFRELEASE(cfStream);
182    CFRELEASE(request);
183	CFRELEASE(postData);
184	CFRELEASE(cfUrl);
185	return ourRtn;
186}
187
188