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