]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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 | * tpTime.c - cert related time functions | |
21 | * | |
b1ab9ed8 A |
22 | */ |
23 | ||
24 | #include "tpTime.h" | |
25 | #include <string.h> | |
26 | #include <stdlib.h> | |
27 | #include <stdio.h> | |
28 | #include <ctype.h> | |
29 | #include <stdbool.h> | |
30 | ||
31 | /* | |
32 | * Given a string containing either a UTC-style or "generalized time" | |
33 | * time string, convert to a CFDateRef. Returns nonzero on | |
34 | * error. | |
35 | */ | |
36 | int timeStringToCfDate( | |
37 | const char *str, | |
38 | unsigned len, | |
39 | CFDateRef *cfDate) | |
40 | { | |
41 | char szTemp[5]; | |
42 | bool isUtc = false; // 2-digit year | |
43 | bool isLocal = false; // trailing timezone offset | |
44 | bool isCssmTime = false; // no trailing 'Z' | |
45 | bool noSeconds = false; | |
427c49bc | 46 | int x; |
b1ab9ed8 A |
47 | unsigned i; |
48 | char *cp; | |
49 | CFGregorianDate gd; | |
50 | CFTimeZoneRef timeZone; | |
51 | CFTimeInterval gmtOff = 0; | |
52 | ||
53 | if((str == NULL) || (len == 0) || (cfDate == NULL)) { | |
54 | return 1; | |
55 | } | |
56 | ||
57 | /* tolerate NULL terminated or not */ | |
58 | if(str[len - 1] == '\0') { | |
59 | len--; | |
60 | } | |
61 | switch(len) { | |
62 | case UTC_TIME_NOSEC_LEN: // 2-digit year, no seconds, not y2K compliant | |
63 | isUtc = true; | |
64 | noSeconds = true; | |
65 | break; | |
66 | case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant | |
67 | isUtc = true; | |
68 | break; | |
69 | case CSSM_TIME_STRLEN: | |
70 | isCssmTime = true; | |
71 | break; | |
72 | case GENERALIZED_TIME_STRLEN: // 4-digit year | |
73 | break; | |
74 | case LOCALIZED_UTC_TIME_STRLEN: // "YYMMDDhhmmssThhmm" (where T=[+,-]) | |
75 | isUtc = 1; | |
76 | // deliberate fallthrough | |
77 | case LOCALIZED_TIME_STRLEN: // "YYYYMMDDhhmmssThhmm" (where T=[+,-]) | |
78 | isLocal = 1; | |
79 | break; | |
80 | default: // unknown format | |
81 | return 1; | |
82 | } | |
83 | ||
84 | cp = (char *)str; | |
85 | ||
86 | /* check that all characters except last (or timezone indicator, if localized) are digits */ | |
87 | for(i=0; i<(len - 1); i++) { | |
88 | if ( !(isdigit(cp[i])) ) | |
89 | if ( !isLocal || !(cp[i]=='+' || cp[i]=='-') ) | |
90 | return 1; | |
91 | } | |
92 | ||
93 | /* check last character is a 'Z' or digit as appropriate */ | |
94 | if(isCssmTime || isLocal) { | |
95 | if(!isdigit(cp[len - 1])) { | |
96 | return 1; | |
97 | } | |
98 | } | |
99 | else { | |
100 | if(cp[len - 1] != 'Z' ) { | |
101 | return 1; | |
102 | } | |
103 | } | |
104 | ||
105 | /* YEAR */ | |
106 | szTemp[0] = *cp++; | |
107 | szTemp[1] = *cp++; | |
108 | if(!isUtc) { | |
109 | /* two more digits */ | |
110 | szTemp[2] = *cp++; | |
111 | szTemp[3] = *cp++; | |
112 | szTemp[4] = '\0'; | |
113 | } | |
114 | else { | |
115 | szTemp[2] = '\0'; | |
116 | } | |
117 | x = atoi( szTemp ); | |
118 | if(isUtc) { | |
119 | /* | |
120 | * 2-digit year. | |
121 | * 0 <= year < 50 : assume century 21 | |
122 | * 50 <= year < 70 : illegal per PKIX | |
123 | * ...though we allow this as of 10/10/02...dmitch | |
124 | * 70 < year <= 99 : assume century 20 | |
125 | */ | |
126 | if(x < 50) { | |
127 | x += 2000; | |
128 | } | |
129 | /* | |
130 | else if(x < 70) { | |
131 | return 1; | |
132 | } | |
133 | */ | |
134 | else { | |
135 | /* century 20 */ | |
136 | x += 1900; | |
137 | } | |
138 | } | |
139 | gd.year = x; | |
140 | ||
141 | /* MONTH */ | |
142 | szTemp[0] = *cp++; | |
143 | szTemp[1] = *cp++; | |
144 | szTemp[2] = '\0'; | |
145 | x = atoi( szTemp ); | |
146 | /* in the string, months are from 1 to 12 */ | |
147 | if((x > 12) || (x <= 0)) { | |
148 | return 1; | |
149 | } | |
150 | gd.month = x; | |
151 | ||
152 | /* DAY */ | |
153 | szTemp[0] = *cp++; | |
154 | szTemp[1] = *cp++; | |
155 | szTemp[2] = '\0'; | |
156 | x = atoi( szTemp ); | |
157 | /* 1..31 in both formats */ | |
158 | if((x > 31) || (x <= 0)) { | |
159 | return 1; | |
160 | } | |
161 | gd.day = x; | |
162 | ||
163 | /* HOUR */ | |
164 | szTemp[0] = *cp++; | |
165 | szTemp[1] = *cp++; | |
166 | szTemp[2] = '\0'; | |
167 | x = atoi( szTemp ); | |
168 | if((x > 23) || (x < 0)) { | |
169 | return 1; | |
170 | } | |
171 | gd.hour = x; | |
172 | ||
173 | /* MINUTE */ | |
174 | szTemp[0] = *cp++; | |
175 | szTemp[1] = *cp++; | |
176 | szTemp[2] = '\0'; | |
177 | x = atoi( szTemp ); | |
178 | if((x > 59) || (x < 0)) { | |
179 | return 1; | |
180 | } | |
181 | gd.minute = x; | |
182 | ||
183 | /* SECOND */ | |
184 | if(noSeconds) { | |
185 | gd.second = 0; | |
186 | } | |
187 | else { | |
188 | szTemp[0] = *cp++; | |
189 | szTemp[1] = *cp++; | |
190 | szTemp[2] = '\0'; | |
191 | x = atoi( szTemp ); | |
192 | if((x > 59) || (x < 0)) { | |
193 | return 1; | |
194 | } | |
195 | gd.second = x; | |
196 | } | |
197 | ||
198 | if (isLocal) { | |
199 | /* ZONE INDICATOR */ | |
200 | switch(*cp++) { | |
201 | case '+': | |
202 | gmtOff = 1; | |
203 | break; | |
204 | case '-': | |
205 | gmtOff = -1; | |
206 | break; | |
207 | default: | |
208 | return 1; | |
209 | } | |
210 | /* ZONE HH OFFSET */ | |
211 | szTemp[0] = *cp++; | |
212 | szTemp[1] = *cp++; | |
213 | szTemp[2] = '\0'; | |
214 | x = atoi( szTemp ) * 60 * 60; | |
215 | gmtOff *= x; | |
216 | /* ZONE MM OFFSET */ | |
217 | szTemp[0] = *cp++; | |
218 | szTemp[1] = *cp++; | |
219 | szTemp[2] = '\0'; | |
220 | x = atoi( szTemp ) * 60; | |
221 | if(gmtOff < 0) { | |
222 | gmtOff -= x; | |
223 | } | |
224 | else { | |
225 | gmtOff += x; | |
226 | } | |
227 | } | |
228 | timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, gmtOff); | |
229 | if (!timeZone) { | |
230 | return 1; | |
231 | } | |
232 | *cfDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone)); | |
233 | CFRelease(timeZone); | |
234 | return 0; | |
235 | } | |
236 | ||
237 | /* | |
238 | * Compare two times. Assumes they're both in GMT. Returns: | |
239 | * -1 if t1 < t2 | |
240 | * 0 if t1 == t2 | |
241 | * 1 if t1 > t2 | |
242 | */ | |
243 | int compareTimes( | |
244 | CFDateRef t1, | |
245 | CFDateRef t2) | |
246 | { | |
247 | switch(CFDateCompare(t1, t2, NULL)) { | |
248 | case kCFCompareLessThan: | |
249 | return -1; | |
250 | case kCFCompareEqualTo: | |
251 | return 0; | |
252 | case kCFCompareGreaterThan: | |
253 | return 1; | |
254 | } | |
255 | /* NOT REACHED */ | |
256 | assert(0); | |
257 | return 0; | |
258 | } | |
259 | ||
260 | /* | |
261 | * Create a time string, in either UTC (2-digit) or or Generalized (4-digit) | |
262 | * year format. Caller mallocs the output string whose length is at least | |
263 | * (UTC_TIME_STRLEN+1), (GENERALIZED_TIME_STRLEN+1), or (CSSM_TIME_STRLEN+1) | |
264 | * respectively. Caller must hold tpTimeLock. | |
265 | */ | |
266 | void timeAtNowPlus(unsigned secFromNow, | |
267 | TpTimeSpec timeSpec, | |
268 | char *outStr) | |
269 | { | |
270 | struct tm utc; | |
271 | time_t baseTime; | |
272 | ||
273 | baseTime = time(NULL); | |
274 | baseTime += (time_t)secFromNow; | |
275 | utc = *gmtime(&baseTime); | |
276 | ||
277 | switch(timeSpec) { | |
278 | case TIME_UTC: | |
279 | /* UTC - 2 year digits - code which parses this assumes that | |
280 | * (2-digit) years between 0 and 49 are in century 21 */ | |
281 | if(utc.tm_year >= 100) { | |
282 | utc.tm_year -= 100; | |
283 | } | |
284 | sprintf(outStr, "%02d%02d%02d%02d%02d%02dZ", | |
285 | utc.tm_year /* + 1900 */, utc.tm_mon + 1, | |
286 | utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec); | |
287 | break; | |
288 | case TIME_GEN: | |
289 | sprintf(outStr, "%04d%02d%02d%02d%02d%02dZ", | |
290 | /* note year is relative to 1900, hopefully it'll have | |
291 | * four valid digits! */ | |
292 | utc.tm_year + 1900, utc.tm_mon + 1, | |
293 | utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec); | |
294 | break; | |
295 | case TIME_CSSM: | |
296 | sprintf(outStr, "%04d%02d%02d%02d%02d%02d", | |
297 | /* note year is relative to 1900, hopefully it'll have | |
298 | * four valid digits! */ | |
299 | utc.tm_year + 1900, utc.tm_mon + 1, | |
300 | utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec); | |
301 | break; | |
302 | } | |
303 | } | |
304 | ||
305 | /* | |
306 | * Convert a time string, which can be in any of three forms (UTC, | |
307 | * generalized, or CSSM_TIMESTRING) into a CSSM_TIMESTRING. Caller | |
308 | * mallocs the result, which must be at least (CSSM_TIME_STRLEN+1) bytes. | |
309 | * Returns nonzero if incoming time string is badly formed. | |
310 | */ | |
311 | int tpTimeToCssmTimestring( | |
312 | const char *inStr, // not necessarily NULL terminated | |
313 | unsigned inStrLen, // not including possible NULL | |
314 | char *outTime) | |
315 | { | |
316 | if((inStrLen == 0) || (inStr == NULL)) { | |
317 | return 1; | |
318 | } | |
319 | outTime[0] = '\0'; | |
320 | switch(inStrLen) { | |
321 | case UTC_TIME_STRLEN: | |
322 | { | |
323 | /* infer century and prepend to output */ | |
324 | char tmp[3]; | |
325 | int year; | |
326 | tmp[0] = inStr[0]; | |
327 | tmp[1] = inStr[1]; | |
328 | tmp[2] = '\0'; | |
329 | year = atoi(tmp); | |
330 | ||
331 | /* | |
332 | * 0 <= year < 50 : assume century 21 | |
333 | * 50 <= year < 70 : illegal per PKIX | |
334 | * 70 < year <= 99 : assume century 20 | |
335 | */ | |
336 | if(year < 50) { | |
337 | /* century 21 */ | |
338 | strcpy(outTime, "20"); | |
339 | } | |
340 | else if(year < 70) { | |
341 | return 1; | |
342 | } | |
343 | else { | |
344 | /* century 20 */ | |
345 | strcpy(outTime, "19"); | |
346 | } | |
347 | memmove(outTime + 2, inStr, inStrLen - 1); // don't copy the Z | |
348 | break; | |
349 | } | |
350 | case CSSM_TIME_STRLEN: | |
351 | memmove(outTime, inStr, inStrLen); // trivial case | |
352 | break; | |
353 | case GENERALIZED_TIME_STRLEN: | |
354 | memmove(outTime, inStr, inStrLen - 1); // don't copy the Z | |
355 | break; | |
356 | ||
357 | default: | |
358 | return 1; | |
359 | } | |
360 | outTime[CSSM_TIME_STRLEN] = '\0'; | |
361 | return 0; | |
362 | } | |
363 | ||
364 |