1/*
2 * Copyright (c) 2000-2001,2011,2014 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19/*
20   File:      MDSAttrUtils.cpp
21
22   Contains:  Stateless functions used by MDSAttrParser.
23
24   Copyright (c) 2001,2011,2014 Apple Inc. All Rights Reserved.
25*/
26
27#include "MDSAttrUtils.h"
28#include <strings.h>
29
30namespace Security
31{
32
33/*
34 * Fill in one CSSM_DB_ATTRIBUTE_DATA with specified data, type and attribute name.
35 * CSSM_DB_ATTRIBUTE_DATA.Value and its referent are new[]'d and copied.
36 * Assumes:
37 *   -- AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING
38 *   -- NumberOfValues = 1
39 */
40void MDSRawValueToDbAttr(
41	const void *value,
42	size_t len,
43	CSSM_DB_ATTRIBUTE_FORMAT attrFormat,	// CSSM_DB_ATTRIBUTE_FORMAT_STRING, etc.
44	const char *attrName,
45	CSSM_DB_ATTRIBUTE_DATA &attr,
46	uint32 numValues)
47{
48	CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo = &attr.Info;
49	attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
50	attrInfo->Label.AttributeName = const_cast<char *>(attrName);
51	attrInfo->AttributeFormat = attrFormat;
52	attr.NumberOfValues = numValues;
53	attr.Value = new CSSM_DATA[1];
54	attr.Value->Data = new uint8[len];
55	attr.Value->Length = len;
56	memcpy(attr.Value->Data, value, len);
57}
58
59
60/*
61 * Free data new[]'d in the above function.
62 */
63void MDSFreeDbRecordAttrs(
64	CSSM_DB_ATTRIBUTE_DATA 	*attrs,
65	unsigned				numAttrs)
66{
67	uint32 i;
68	for(i=0; i<numAttrs; i++) {
69		assert(attrs->Value != NULL);
70		delete [] attrs->Value->Data;
71		attrs->Value->Data = NULL;
72		attrs->Value->Length = 0;
73		delete [] attrs->Value;
74		attrs->Value = NULL;
75		attrs++;
76	}
77}
78
79/* safely get a new[]'d C string from a CFString */
80char *MDSCFStringToCString(
81	CFStringRef cfStr)
82{
83	char *rtn = NULL;
84	CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfStr), kCFStringEncodingUTF8) + 1/*nul terminator*/;
85	rtn = new char[len];
86	if(rtn) {
87		CFStringGetCString(cfStr, rtn, len, kCFStringEncodingUTF8);
88	}
89	return rtn;
90}
91
92/* copy a new[]'d C string from a C string */
93char *MDSCopyCstring(
94	const char *inStr)
95{
96	char *outStr = new char[::strlen(inStr) + 1];
97	strcpy(outStr, inStr);
98	return outStr;
99}
100
101/*
102 * Given a CFTypeRef which is either a CFString, a CFNumber, or a CFBoolean,
103 * do our best to convert it to a uint32. If it's a CFString, we'll use a
104 * MDSNameValuePair to convert it. CFStrings expressed as decimal numbers
105 * are also converted properly. (MAYBE we'll convert hex strings too...TBD...)
106 * Returns true if conversion was successful.
107 */
108bool MDSCfTypeToUInt32(
109	CFTypeRef cfValue,
110	const MDSNameValuePair *nameValues,	// optional for converting strings to numbers
111	const char *key,					// for debug logging only
112	uint32 &iValue,						// RETURNED
113    size_t &iValueLen)					// RETURNED
114{
115	assert(cfValue != NULL);
116	CFTypeID valueType = CFGetTypeID(cfValue);
117	if(valueType == CFStringGetTypeID()) {
118        uint32 tmpValue = 0;
119		CSSM_RETURN crtn = MDSStringToUint32((CFStringRef)cfValue,
120			nameValues, tmpValue);
121		if(crtn) {
122			MPDebug("cfTypeToInt: key %s uint32 form, string data (%s), "
123				"bad conv", key,
124				CFStringGetCStringPtr((CFStringRef)cfValue,
125					kCFStringEncodingUTF8));
126			return false;
127		}
128        iValue = tmpValue;
129        iValueLen = sizeof(tmpValue);
130		return true;
131	}	/* stored as string */
132	else if(valueType == CFNumberGetTypeID()) {
133        int64_t tmpValue = 0;
134		/* be paranoid - there is no unsigned type for CFNumber */
135		CFNumberRef cfNum = (CFNumberRef)cfValue;
136		CFNumberType numType = CFNumberGetType(cfNum);
137		switch(numType) {
138			case kCFNumberSInt8Type:
139                iValueLen = 1; break;
140			case kCFNumberSInt16Type:
141                iValueLen = 2; break;
142			case kCFNumberSInt32Type:
143                iValueLen = 4; break;
144			case kCFNumberSInt64Type:	// apparently the default
145                // There are no 64-bit types in CDSA, so assume this is how
146                // CF encoded an unsigned 32-bit int whose high bit was set.
147                iValueLen = 4; break;
148			case kCFNumberCharType:
149                iValueLen = sizeof(char); break;
150			case kCFNumberShortType:
151                iValueLen = sizeof(short); break;
152			case kCFNumberIntType:
153                iValueLen = sizeof(int); break;
154			case kCFNumberLongType:
155				MPDebug("Warning: MDS key %s encoded kCFNumberLongType", key);
156                iValueLen = sizeof(long); break;
157			default:
158				MPDebug("MDS cfTypeToInt: Bad CFNumber type (%ld) key %s", numType, key);
159				return false;
160		}
161		Boolean brtn = CFNumberGetValue(cfNum, numType, &tmpValue);
162		if(!brtn) {
163			MPDebug("MDS cfTypeToInt: Bad CFNumber conversion");
164			return false;
165		}
166        iValue = uint32(tmpValue);
167		return true;
168	}	/* stored as number */
169	else if(valueType == CFBooleanGetTypeID()) {
170		Boolean b = CFBooleanGetValue((CFBooleanRef)cfValue);
171		iValue = b ? 1 : 0;
172        iValueLen = sizeof(iValue);
173		return true;
174	}
175	else {
176		MPDebug("MDS cfTypeToInt: key %s, uint64 form, bad CF type (%d)",
177			key, (int)valueType);
178		return false;
179	}
180}
181
182/*
183 * Insert a record, defined by a CSSM_DB_ATTRIBUTE_DATA array, into specified
184 * DL and DB. Returns true on success.
185 */
186bool MDSInsertRecord(
187	const CSSM_DB_ATTRIBUTE_DATA 	*inAttr,
188	unsigned						numAttrs,
189	CSSM_DB_RECORDTYPE				recordType,
190	MDSSession						&dl,
191	CSSM_DB_HANDLE					dbHand)
192{
193	CSSM_DB_RECORD_ATTRIBUTE_DATA 	recordAttrData;
194	CSSM_DB_UNIQUE_RECORD_PTR		uid = NULL;
195	bool							ourRtn = true;
196
197	recordAttrData.DataRecordType = recordType;
198	recordAttrData.SemanticInformation = 0;
199	recordAttrData.NumberOfAttributes = numAttrs;
200	recordAttrData.AttributeData =
201		const_cast<CSSM_DB_ATTRIBUTE_DATA_PTR>(inAttr);
202
203	try {
204		dl.DataInsert(dbHand,
205			recordType,
206			&recordAttrData,
207			NULL,
208			uid);
209	}
210	catch (const CssmError &cerr) {
211		MPDebug("MDSInsertRecord: DataInsert: %d", cerr.error);
212		ourRtn = false;
213	}
214	catch(...) {
215		MPDebug("MDSInsertRecord: DataInsert: unknown exception");
216		ourRtn = false;
217	}
218	if(uid != NULL) {
219		dl.FreeUniqueRecord(dbHand, *uid);
220	}
221	return ourRtn;
222}
223
224/*
225 * Convert a number expressed as a CFString to a uint32 using the specified
226 * name/value conversion table. The string may have multiple fields from that
227 * table, ORd together in normal C syntax. Like
228 *
229 *      CSSM_SERVICE_CSP | CSSM_SERVICE_DL
230 *
231 * Individual tokens can also be expressed numerically, either in decimal or
232 * (if prefaced by "0x" hex. Numeric tokens and symbolic string tokens can
233 * be intermixed in the same incoming string.
234 *
235 * Individual tokens can be prefixed with "<<" indicating that the indicated
236 * value is to be shifted 16 bits. Cf. CL Primary Relation, {Cert,Crl}TypeFormat.
237 * This applies to both numeric and string tokens.
238 */
239CSSM_RETURN MDSStringToUint32(
240	CFStringRef str,
241	const MDSNameValuePair *table,		// optional, string must be decimal
242	uint32 &value)
243{
244	char *cstr = MDSCFStringToCString(str);
245	if(cstr == NULL) {
246		/* should "never" happen...right? */
247		MPDebug("MDSStringToUint32: CFString conversion error");
248		return CSSMERR_CSSM_MDS_ERROR;
249	}
250
251	char tokenStr[200];
252	char *src = cstr;
253	char *dst = tokenStr;
254	char c;
255	CSSM_RETURN crtn = CSSM_OK;
256
257	value = 0;
258	while(*src != '\0') {
259		/* Get one token from src --> tokenStr[] */
260		/* First skip whitespace and '|' */
261		for( ; *src != '\0'; src++) {
262			c = *src;
263			if(!isspace(c) && (c != '|')) {
264				/* first char of token */
265				*dst++ = c;
266				src++;
267				break;
268			}
269		}
270		if((*src == '\0') && (dst == tokenStr)) {
271			/* done */
272			break;
273		}
274
275		/* dst[-1] is the first good character of token; copy until
276		 * space or '|' */
277		for( ; *src != '\0'; src++) {
278			c = *src;
279			if(isspace(c) || (c == '|')) {
280				break;
281			}
282			else {
283				*dst++ = c;
284			}
285		}
286
287		/* NULL terminate token string, convert to numeric value */
288		*dst = '\0';
289		uint32 tokenVal = 0;
290		CSSM_RETURN crtn = MDSAttrNameToValue(tokenStr, table, tokenVal);
291		if(crtn) {
292			/* punt */
293			break;
294		}
295		value |= tokenVal;
296
297		/* restart */
298		dst = tokenStr;
299	}
300	delete [] cstr;
301	return crtn;
302}
303
304} // end namespace Security
305