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