1/*
2 * Copyright (c) 2002,2011-2012,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 * cuTimeStr.cpp - time string routines
20 */
21#include "cuTimeStr.h"
22#include "cuCdsaUtils.h"
23#include <string.h>
24#include <stdlib.h>
25#include <stdio.h>
26#include <ctype.h>
27#include <pthread.h>
28
29/*
30 * Given a string containing either a UTC-style or "generalized time"
31 * time string, convert to a struct tm (in GMT/UTC). Returns nonzero on
32 * error.
33 */
34int cuTimeStringToTm(
35	const char			*str,
36	unsigned			len,
37	struct tm			*tmp)
38{
39	char 		szTemp[5];
40	unsigned 	isUtc = 0;
41	unsigned	noSeconds = 0;
42	int             x;
43	unsigned 	i;
44	char 		*cp;
45
46	if((str == NULL) || (len == 0) || (tmp == NULL)) {
47    	return 1;
48  	}
49
50  	/* tolerate NULL terminated or not */
51  	if(str[len - 1] == '\0') {
52  		len--;
53  	}
54  	switch(len) {
55		case UTC_TIME_NOSEC_LEN:		// 2-digit year, no seconds, not y2K compliant
56			isUtc = 1;
57			noSeconds = 1;
58			break;
59  		case UTC_TIME_STRLEN:			// 2-digit year, not Y2K compliant
60  			isUtc = 1;
61  			break;
62  		case GENERALIZED_TIME_STRLEN:	// 4-digit year
63  			break;
64  		default:						// unknown format
65  			return 1;
66  	}
67
68  	cp = (char *)str;
69
70	/* check that all characters except last are digits */
71	for(i=0; i<(len - 1); i++) {
72		if ( !(isdigit(cp[i])) ) {
73		  	return 1;
74		}
75	}
76
77  	/* check last character is a 'Z' */
78  	if(cp[len - 1] != 'Z' )	{
79		return 1;
80  	}
81
82  	/* YEAR */
83	szTemp[0] = *cp++;
84	szTemp[1] = *cp++;
85	if(!isUtc) {
86		/* two more digits */
87		szTemp[2] = *cp++;
88		szTemp[3] = *cp++;
89		szTemp[4] = '\0';
90	}
91	else {
92		szTemp[2] = '\0';
93	}
94	x = atoi( szTemp );
95	if(isUtc) {
96		/*
97		 * 2-digit year.
98		 *   0  <= year <  50 : assume century 21
99		 *   50 <= year <  70 : illegal per PKIX, though we tolerate
100		 *   70 <  year <= 99 : assume century 20
101		 */
102		if(x < 50) {
103			x += 2000;
104		}
105		/*
106		else if(x < 70) {
107			return 1;
108		}
109		*/
110		else {
111			/* century 20 */
112			x += 1900;
113		}
114	}
115  	/* by definition - tm_year is year - 1900 */
116  	tmp->tm_year = x - 1900;
117
118  	/* MONTH */
119	szTemp[0] = *cp++;
120	szTemp[1] = *cp++;
121	szTemp[2] = '\0';
122	x = atoi( szTemp );
123	/* in the string, months are from 1 to 12 */
124	if((x > 12) || (x <= 0)) {
125    	return 1;
126	}
127	/* in a tm, 0 to 11 */
128  	tmp->tm_mon = x - 1;
129
130 	/* DAY */
131	szTemp[0] = *cp++;
132	szTemp[1] = *cp++;
133	szTemp[2] = '\0';
134	x = atoi( szTemp );
135	/* 1..31 in both formats */
136	if((x > 31) || (x <= 0)) {
137		return 1;
138	}
139  	tmp->tm_mday = x;
140
141	/* HOUR */
142	szTemp[0] = *cp++;
143	szTemp[1] = *cp++;
144	szTemp[2] = '\0';
145	x = atoi( szTemp );
146	if((x > 23) || (x < 0)) {
147		return 1;
148	}
149	tmp->tm_hour = x;
150
151  	/* MINUTE */
152	szTemp[0] = *cp++;
153	szTemp[1] = *cp++;
154	szTemp[2] = '\0';
155	x = atoi( szTemp );
156	if((x > 59) || (x < 0)) {
157		return 1;
158	}
159  	tmp->tm_min = x;
160
161  	/* SECOND */
162	if(noSeconds) {
163		tmp->tm_sec = 0;
164	}
165	else {
166		szTemp[0] = *cp++;
167		szTemp[1] = *cp++;
168		szTemp[2] = '\0';
169		x = atoi( szTemp );
170		if((x > 59) || (x < 0)) {
171			return 1;
172		}
173		tmp->tm_sec = x;
174	}
175	return 0;
176}
177
178#define MAX_TIME_STR_LEN  	30
179
180/* protects time(), gmtime() */
181static pthread_mutex_t timeMutex = PTHREAD_MUTEX_INITIALIZER;
182
183char *cuTimeAtNowPlus(int secFromNow,
184	timeSpec spec)
185{
186	struct tm utc;
187	char *outStr;
188	time_t baseTime;
189
190	pthread_mutex_lock(&timeMutex);
191	baseTime = time(NULL);
192	baseTime += (time_t)secFromNow;
193	utc = *gmtime(&baseTime);
194	pthread_mutex_unlock(&timeMutex);
195
196	outStr = (char *)APP_MALLOC(MAX_TIME_STR_LEN);
197
198	switch(spec) {
199		case TIME_UTC:
200			/* UTC - 2 year digits - code which parses this assumes that
201			 * (2-digit) years between 0 and 49 are in century 21 */
202			if(utc.tm_year >= 100) {
203				utc.tm_year -= 100;
204			}
205			sprintf(outStr, "%02d%02d%02d%02d%02d%02dZ",
206				utc.tm_year /* + 1900 */, utc.tm_mon + 1,
207				utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
208			break;
209		case TIME_GEN:
210			sprintf(outStr, "%04d%02d%02d%02d%02d%02dZ",
211				/* note year is relative to 1900, hopefully it'll
212				 * have four valid digits! */
213				utc.tm_year + 1900, utc.tm_mon + 1,
214				utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
215			break;
216		case TIME_CSSM:
217			sprintf(outStr, "%04d%02d%02d%02d%02d%02d",
218				/* note year is relative to 1900, hopefully it'll have
219				 * four valid digits! */
220				utc.tm_year + 1900, utc.tm_mon + 1,
221				utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
222			break;
223	}
224	return outStr;
225}
226
227/*
228 * Convert a CSSM_X509_TIME, which can be in any of three forms (UTC,
229 * generalized, or CSSM_TIMESTRING) into a CSSM_TIMESTRING. Caller
230 * must free() the result. Returns NULL if x509time is badly formed.
231 */
232char *cuX509TimeToCssmTimestring(
233	const CSSM_X509_TIME 	*x509Time,
234	unsigned				*rtnLen)		// for caller's convenience
235{
236	int len = (int)x509Time->time.Length;
237	const char *inStr = (char *)x509Time->time.Data;
238											// not NULL terminated!
239	char *rtn;
240
241	*rtnLen = 0;
242	if((len == 0) || (inStr == NULL)) {
243		return NULL;
244	}
245	rtn = (char *)malloc(CSSM_TIME_STRLEN + 1);
246	rtn[0] = '\0';
247	switch(len) {
248		case UTC_TIME_STRLEN:
249		{
250			/* infer century and prepend to output */
251			char tmp[3];
252			int year;
253			tmp[0] = inStr[0];
254			tmp[1] = inStr[1];
255			tmp[2] = '\0';
256			year = atoi(tmp);
257
258			/*
259			 *   0  <= year <  50 : assume century 21
260			 *   50 <= year <  70 : illegal per PKIX
261			 *   70 <  year <= 99 : assume century 20
262			 */
263			if(year < 50) {
264				/* century 21 */
265				strcpy(rtn, "20");
266			}
267			else if(year < 70) {
268				free(rtn);
269				return NULL;
270			}
271			else {
272				/* century 20 */
273				strcpy(rtn, "19");
274			}
275			memmove(rtn + 2, inStr, len - 1);		// don't copy the Z
276			break;
277		}
278		case CSSM_TIME_STRLEN:
279			memmove(rtn, inStr, len);				// trivial case
280			break;
281		case GENERALIZED_TIME_STRLEN:
282			memmove(rtn, inStr, len - 1);			// don't copy the Z
283			break;
284
285		default:
286			free(rtn);
287			return NULL;
288	}
289	rtn[CSSM_TIME_STRLEN] = '\0';
290	*rtnLen = CSSM_TIME_STRLEN;
291	return rtn;
292}
293
294