1/*
2 * Copyright (c) 2002-2003 Apple Computer, 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.
7 * Please obtain a copy of the License at http://www.apple.com/publicsource
8 * and read it before 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
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights
16 * and limitations under the License.
17 */
18
19/*
20 * cuOidParser.cpp - parse an Intel-style OID, with the assistance
21 * of dumpasn1.cfg
22 */
23
24#include <Security/cssmtype.h>
25#include <string.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include "cuOidParser.h"
29#include "cuFileIo.h"
30#include <fcntl.h>
31#include <errno.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <unistd.h>
37
38/* get config file from .. or from . */
39#define 		CONFIG_FILE_NAME	"dumpasn1.cfg"
40static const char 	*CONFIG_FILE1 = 	"../"CONFIG_FILE_NAME;
41static const char 	*CONFIG_FILE2 = 	CONFIG_FILE_NAME;
42/* or from here via getenv */
43#define 		CONFIG_FILE_ENV 	"LOCAL_BUILD_DIR"
44
45static const char 	*OID_ENTRY_START = "OID = ";
46static const char 	*OID_DESCR_START = "Description = ";
47/*
48 * Read entire file with extra bytes left over in the mallocd buffer.
49 */
50static
51int readFileExtra(
52	const char		*fileName,
53	unsigned		extraBytes,
54	unsigned char	**bytes,		// mallocd and returned
55	CSSM_SIZE		*numBytes)		// returned
56{
57	int rtn;
58	int fd;
59	unsigned char *buf;
60	struct stat	sb;
61	size_t size;
62
63	*numBytes = 0;
64	*bytes = NULL;
65	fd = open(fileName, O_RDONLY, 0);
66	if(fd < 0) {
67		return 1;
68	}
69	rtn = fstat(fd, &sb);
70	if(rtn) {
71		goto errOut;
72	}
73	size = (size_t)sb.st_size;
74	buf = (unsigned char *)malloc(size + extraBytes);
75	if(buf == NULL) {
76		rtn = ENOMEM;
77		goto errOut;
78	}
79	rtn = (int)lseek(fd, 0, SEEK_SET);
80	if(rtn < 0) {
81		goto errOut;
82	}
83	rtn = (int)read(fd, buf, (size_t)size);
84	if(rtn != (int)size) {
85		if(rtn >= 0) {
86			printf("readFile: short read\n");
87		}
88		rtn = EIO;
89	}
90	else {
91		rtn = 0;
92		*bytes = buf;
93		*numBytes = size;
94	}
95errOut:
96	close(fd);
97	return rtn;
98}
99
100/*
101 * Attempt to read dumpasn1.cfg from various places. If we can't find it,
102 * printOid() function will just print raw bytes as it
103 * would if the .cfg file did not contain the desired OID.
104 */
105static CSSM_DATA_PTR readConfig()
106{
107	CSSM_DATA_PTR	configData = NULL;
108	int				rtn;
109
110	configData = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
111	if(configData == NULL) {
112		return NULL;
113	}
114	/* malloc one extra byte, we'll null it later */
115	rtn = readFileExtra(CONFIG_FILE1, 1, &configData->Data,
116		&configData->Length);
117	if(rtn) {
118		rtn = readFileExtra(CONFIG_FILE2, 1, &configData->Data,
119				&configData->Length);
120	}
121	if(rtn) {
122		char fileName[100];
123		char *localBuildDir  = getenv(CONFIG_FILE_ENV);
124		if(localBuildDir == NULL) {
125			rtn = 1;
126		}
127		else {
128			sprintf(fileName,  "%s/%s", localBuildDir, CONFIG_FILE_NAME);
129			rtn = readFileExtra(fileName, 1, &configData->Data,
130				&configData->Length);
131		}
132	}
133	if(rtn == 0) {
134		/* make the whole shebang one long C string */
135		configData->Data[configData->Length++] = '\0';
136		return configData;
137	}
138	else {
139		free(configData);
140		return NULL;
141	}
142}
143
144/*
145 * The heart of this module.
146 *
147 * -- Convert Intel-style OID to a string which might be found
148 *    in the config file
149 * -- search config file for that string
150 * -- if found, use that entry in config file to output meaningful
151 *    string and return CSSM_TRUE. Else return CSSM_FALSE.
152 */
153static CSSM_BOOL parseOidWithConfig(
154	const CSSM_DATA_PTR configData,
155	const CSSM_OID_PTR	oid,
156	char				*strBuf)
157{
158	char				*fullOidStr = NULL;
159	char				*ourEntry = NULL;
160	char				*nextEntry = NULL;
161	char				*descStart = NULL;
162	char				*cp;
163	unsigned			i;
164	CSSM_BOOL			brtn;
165	char				*nextCr;		// next CR if any
166	char				*nextNl;		// next NL if any
167	char				*eol;			// end of line
168	int					len;
169
170	if(configData == NULL) {
171		return CSSM_FALSE;
172	}
173
174	/* cook up a full OID string, with tag and length */
175	fullOidStr = (char *)malloc((3 * oid->Length) +
176												// 2 chars plus space per byte
177		strlen(OID_ENTRY_START) +				// "OID = "
178		6 +										// 06 xx - tag and length
179		1);										// NULL
180	if(fullOidStr == NULL) {
181		return CSSM_FALSE;
182	}
183	/* subsequent errors to errOut: */
184
185	sprintf(fullOidStr, "OID = 06 %02X", (unsigned)oid->Length);
186	cp = fullOidStr + strlen(fullOidStr);
187	for(i=0; i<oid->Length; i++) {
188		/* move cp to current end of string */
189		cp += strlen(cp);
190		/* add one byte */
191		sprintf(cp, " %02X", oid->Data[i]);
192	}
193
194	/*
195	 * Let's play it loose and assume that there are no embedded NULLs
196	 * in the config file. Thus we can use the spiffy string functions
197	 * in stdlib.
198	 */
199	ourEntry = strstr((char *)configData->Data, fullOidStr);
200	if(ourEntry == NULL) {
201		brtn = CSSM_FALSE;
202		goto errOut;
203	}
204
205	/* get position of NEXT full entry - may be NULL (end of file) */
206	nextEntry = strstr(ourEntry+1, OID_ENTRY_START);
207
208	/* get position of our entry's description line */
209	descStart = strstr(ourEntry+1, OID_DESCR_START);
210
211	/* handle not found/overflow */
212	if( (descStart == NULL) ||			// no more description lines
213	    ( (descStart > nextEntry) &&  	// no description in THIS entry
214	      (nextEntry != NULL) ) ) {		// make sure this is valid
215		brtn = CSSM_FALSE;
216		goto errOut;
217	}
218
219	/* set descStart to after the leader */
220	descStart += strlen(OID_DESCR_START);
221
222	/*
223	 * descStart points to the text we're interested in.
224	 * First find end of line, any style.
225	 */
226	nextNl = strchr(descStart, '\n');
227	nextCr = strchr(descStart, '\r');
228	if((nextNl == NULL) && (nextCr == NULL)) {
229		/* no line terminator, go to eof */
230		eol = (char *)configData->Data + configData->Length;
231	}
232	else if(nextCr == NULL) {
233		eol = nextNl;
234	}
235	else if(nextNl == NULL) {
236		eol = nextCr;
237	}
238	else if(nextNl < nextCr) {
239		/* both present, take first one */
240		eol = nextNl;
241	}
242	else {
243		eol = nextCr;
244	}
245
246	/* caller's string buf = remainder of description line */
247	len = (int)(eol - descStart);
248	if(len > (OID_PARSER_STRING_SIZE - 1)) {
249		/* fixed-length output buf, avoid overflow */
250		len = OID_PARSER_STRING_SIZE - 1;
251	}
252	memcpy(strBuf, descStart, len);
253	strBuf[len] = '\0';
254	brtn = CSSM_TRUE;
255errOut:
256	if(fullOidStr != NULL) {
257		free(fullOidStr);
258	}
259	return brtn;
260}
261
262/*** OidParser class ***/
263OidParser::OidParser(bool noConfig)
264{
265	if(noConfig) {
266		configData = NULL;
267	}
268	else {
269		configData = readConfig();
270	}
271}
272
273OidParser::~OidParser()
274{
275	if(configData == NULL) {
276		return;
277	}
278	if(configData->Data != NULL) {
279		free(configData->Data);
280	}
281	free(configData);
282}
283
284/*
285 * Parse an Intel-style OID, generating a C string in caller-supplied buffer.
286 */
287void OidParser::oidParse(
288	const unsigned char	*oidp,
289	unsigned			oidLen,
290	char 				*strBuf)
291{
292	unsigned i;
293	CSSM_OID oid;
294
295	oid.Data = (uint8  *)oidp;
296	oid.Length = oidLen;
297
298	if((oidLen == 0) || (oidp == NULL)) {
299		strcpy(strBuf, "EMPTY");
300		return;
301	}
302	if(parseOidWithConfig(configData, &oid, strBuf) == CSSM_FALSE) {
303		/* no config file, just dump the bytes */
304		char cbuf[8];
305
306		sprintf(strBuf, "OID : < 06 %02X ", (unsigned)oid.Length);
307		for(i=0; i<oid.Length; i++) {
308			sprintf(cbuf, "%02X ", oid.Data[i]);
309			strcat(strBuf, cbuf);
310		}
311		strcat(strBuf, ">");
312	}
313}
314
315
316