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