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