]> git.saurik.com Git - apple/security.git/blob - libsecurity_apple_x509_tp/lib/tpTime.c
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_apple_x509_tp / lib / tpTime.c
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;
47 unsigned x;
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