1/*
2 * crlNetwork.cpp - Network support for crlTool
3 */
4
5#include "crlNetwork.h"
6#include <CoreFoundation/CoreFoundation.h>
7#include <CoreServices/CoreServices.h>
8#include <security_cdsa_utils/cuEnc64.h>
9#include <stdlib.h>
10#include <Security/cssmapple.h>
11#include <LDAP/ldap.h>
12
13#define ocspdErrorLog(args...)		printf(args)
14
15#pragma mark ----- LDAP fetch -----
16
17/*
18 * LDAP attribute names, used if not present in URI.
19 */
20#define LDAP_ATTR_CERT		"cacertificate;binary"
21#define LDAP_ATTR_CRL		"certificaterevocationlist;binary"
22
23/*
24 * Default LDAP options.
25 */
26#define LDAP_REFERRAL_DEFAULT	LDAP_OPT_ON
27
28static CSSM_RETURN ldapRtnToCssm(
29	int rtn)
30{
31	switch(rtn) {
32		case LDAP_SERVER_DOWN:
33		case LDAP_TIMEOUT:
34		case LDAP_CONNECT_ERROR:
35			return CSSMERR_APPLETP_CRL_SERVER_DOWN;
36		case LDAP_PARAM_ERROR:
37		case LDAP_FILTER_ERROR:
38			return CSSMERR_APPLETP_CRL_BAD_URI;
39		default:
40			return CSSMERR_APPLETP_CRL_NOT_FOUND;
41	}
42}
43
44static CSSM_RETURN ldapFetch(
45	const CSSM_DATA 	&url,
46	LF_Type				lfType,
47	CSSM_DATA			&fetched)	// mallocd and RETURNED
48{
49	BerValue 		**value = NULL;
50	LDAPURLDesc 	*urlDesc = NULL;
51	int 			rtn;
52	LDAPMessage 	*msg = NULL;
53	LDAP 			*ldap = NULL;
54	LDAPMessage 	*entry = NULL;
55	bool 			mallocdString = false;
56	char 			*urlStr;
57	int 			numEntries;
58	CSSM_RETURN 	ourRtn = CSSM_OK;
59	/* attr input to ldap_search_s() */
60	char			*attrArray[2];
61	char			**attrArrayP = NULL;
62
63	/* don't assume URL string is NULL terminated */
64	if(url.Data[url.Length - 1] == '\0') {
65		urlStr = (char *)url.Data;
66	}
67	else {
68		urlStr = (char *)malloc(url.Length + 1);
69		memmove(urlStr, url.Data, url.Length);
70		urlStr[url.Length] = '\0';
71		mallocdString = true;
72	}
73
74	/* break up the URL into something usable */
75	rtn = ldap_url_parse(urlStr, &urlDesc);
76	if(rtn) {
77		ocspdErrorLog("ldap_url_parse returned %d", rtn);
78		return CSSMERR_APPLETP_CRL_BAD_URI;
79	}
80
81	/*
82	 * Determine what attr we're looking for.
83	 */
84	if((urlDesc->lud_attrs != NULL) &&		// attrs present in URL
85	   (urlDesc->lud_attrs[0] != NULL) &&	// at least one attr present
86	   (urlDesc->lud_attrs[1] == NULL))	{
87		/*
88		 * Exactly one attr present in the caller-specified URL;
89		 * assume that this is exactly what we want.
90		 */
91		attrArrayP = &urlDesc->lud_attrs[0];
92	}
93	else {
94		/* use caller-specified attr */
95		switch(lfType) {
96			case LT_Crl:
97				attrArray[0] = (char *)LDAP_ATTR_CRL;
98				break;
99			case LT_Cert:
100				attrArray[0] = (char *)LDAP_ATTR_CERT;
101				break;
102			default:
103				printf("***ldapFetch screwup: bogus lfType (%d)\n",
104					(int)lfType);
105				return CSSMERR_CSSM_INTERNAL_ERROR;
106		}
107		attrArray[1] = NULL;
108		attrArrayP = &attrArray[0];
109	}
110
111	/* establish connection */
112	rtn = ldap_initialize(&ldap, urlStr);
113	if(rtn) {
114		ocspdErrorLog("ldap_initialize returned %d\n", rtn);
115		return ldapRtnToCssm(rtn);
116	}
117	/* subsequent errors to cleanup: */
118	rtn = ldap_simple_bind_s(ldap, NULL, NULL);
119	if(rtn) {
120		ocspdErrorLog("ldap_simple_bind_s returned %d\n", rtn);
121		ourRtn = ldapRtnToCssm(rtn);
122		goto cleanup;
123	}
124
125	rtn = ldap_set_option(ldap, LDAP_OPT_REFERRALS, LDAP_REFERRAL_DEFAULT);
126	if(rtn) {
127		ocspdErrorLog("ldap_set_option(referrals) returned %d\n", rtn);
128		ourRtn = ldapRtnToCssm(rtn);
129		goto cleanup;
130	}
131
132	rtn = ldap_search_s(
133		ldap,
134		urlDesc->lud_dn,
135		LDAP_SCOPE_SUBTREE,
136		urlDesc->lud_filter,
137		urlDesc->lud_attrs,
138		0, 			// attrsonly
139		&msg);
140	if(rtn) {
141		ocspdErrorLog("ldap_search_s returned %d\n", rtn);
142		ourRtn = ldapRtnToCssm(rtn);
143		goto cleanup;
144	}
145
146	/*
147	 * We require exactly one entry (for now).
148	 */
149	numEntries = ldap_count_entries(ldap, msg);
150	if(numEntries != 1) {
151		ocspdErrorLog("tpCrlViaLdap: numEntries %d\n", numEntries);
152		ourRtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
153		goto cleanup;
154	}
155
156	entry = ldap_first_entry(ldap, msg);
157	value = ldap_get_values_len(ldap, msg, attrArrayP[0]);
158	if(value == NULL) {
159		ocspdErrorLog("Error on ldap_get_values_len\n");
160		ourRtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
161		goto cleanup;
162	}
163
164	fetched.Length = value[0]->bv_len;
165	fetched.Data = (uint8 *)malloc(fetched.Length);
166	memmove(fetched.Data, value[0]->bv_val, fetched.Length);
167
168	ldap_value_free_len(value);
169	ourRtn = CSSM_OK;
170cleanup:
171	if(msg) {
172		ldap_msgfree(msg);
173	}
174	if(mallocdString) {
175		free(urlStr);
176	}
177	ldap_free_urldesc(urlDesc);
178	rtn = ldap_unbind(ldap);
179	if(rtn) {
180		ocspdErrorLog("Error %d on ldap_unbind\n", rtn);
181		/* oh well */
182	}
183	return ourRtn;
184}
185
186#pragma mark ----- HTTP fetch via GET -----
187
188/* fetch via HTTP */
189static CSSM_RETURN httpFetch(
190	const CSSM_DATA 	&url,
191	CSSM_DATA			&fetched)	// mallocd in alloc space and RETURNED
192{
193	/* trim off possible NULL terminator */
194	CSSM_DATA theUrl = url;
195	if(theUrl.Data[theUrl.Length - 1] == '\0') {
196		theUrl.Length--;
197	}
198	CFURLRef cfUrl = CFURLCreateWithBytes(NULL,
199		theUrl.Data, theUrl.Length,
200		kCFStringEncodingUTF8,		// right?
201		//kCFStringEncodingASCII,		// right?
202		NULL);						// this is absolute path
203	if(cfUrl == NULL) {
204		ocspdErrorLog("CFURLCreateWithBytes returned NULL\n");
205		return CSSMERR_APPLETP_CRL_BAD_URI;
206	}
207	CFDataRef urlData = NULL;
208	SInt32 errorCode;
209	Boolean brtn = CFURLCreateDataAndPropertiesFromResource(NULL,
210		cfUrl,
211		&urlData,
212		NULL,			// no properties
213		NULL,
214		&errorCode);
215	CFRelease(cfUrl);
216	if(!brtn) {
217		ocspdErrorLog("CFURLCreateDataAndPropertiesFromResource err: %d\n",
218			(int)errorCode);
219		if(urlData) {
220			return CSSMERR_APPLETP_NETWORK_FAILURE;
221		}
222	}
223	if(urlData == NULL) {
224		ocspdErrorLog("CFURLCreateDataAndPropertiesFromResource: no data\n");
225		return CSSMERR_APPLETP_NETWORK_FAILURE;
226	}
227	CFIndex len = CFDataGetLength(urlData);
228	fetched.Data = (uint8 *)malloc(len);
229	fetched.Length = len;
230	memmove(fetched.Data, CFDataGetBytePtr(urlData), len);
231	CFRelease(urlData);
232	return CSSM_OK;
233}
234
235/* Fetch cert or CRL from net, we figure out the schema */
236CSSM_RETURN crlNetFetch(
237	const CSSM_DATA 	*url,
238	LF_Type				lfType,
239	CSSM_DATA			*fetched)	// mallocd in alloc space and RETURNED
240{
241	if(url->Length < 5) {
242		return CSSMERR_APPLETP_CRL_BAD_URI;
243	}
244	if(!strncmp((char *)url->Data, "ldap:", 5)) {
245		return ldapFetch(*url, lfType, *fetched);
246	}
247	if(!strncmp((char *)url->Data, "http:", 5) ||
248	   !strncmp((char *)url->Data, "https:", 6)) {
249		return httpFetch(*url, *fetched);
250	}
251	return CSSMERR_APPLETP_CRL_BAD_URI;
252}
253
254