]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/tpTime.c
Security-59306.120.7.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 /*
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;
46 int x;
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 TP_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 TP_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 TP_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