]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/cssmdatetime.cpp
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / cssmdatetime.cpp
1 /*
2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * cssmdatetime.cpp -- CSSM date and time utilities for the Mac
24 */
25
26 #ifdef __MWERKS__
27 #define _CPP_CSSM_DATE_TIME_UTILS
28 #endif
29
30 #include "cssmdatetime.h"
31
32 #include <string.h>
33 #include <stdio.h>
34 #include <security_utilities/errors.h>
35 #include <CoreFoundation/CFDate.h>
36 #include <CoreFoundation/CFTimeZone.h>
37 #include <ctype.h>
38 #include <stdlib.h>
39 #include <Security/SecBase.h>
40 namespace Security
41 {
42
43 namespace CSSMDateTimeUtils
44 {
45
46 #define UTC_TIME_NOSEC_LEN 11
47 #define UTC_TIME_STRLEN 13
48 #define GENERALIZED_TIME_STRLEN 15
49 #define LOCALIZED_UTC_TIME_STRLEN 17
50 #define LOCALIZED_TIME_STRLEN 19
51 #define MAX_TIME_STR_LEN 30
52
53
54 void
55 GetCurrentMacLongDateTime(sint64 &outMacDate)
56 {
57 CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
58 CFAbsoluteTime absTime = CFAbsoluteTimeGetCurrent();
59 absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
60 CFRelease(timeZone);
61 outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
62 }
63
64 void
65 TimeStringToMacSeconds (const CSSM_DATA &inUTCTime, uint32 &ioMacDate)
66 {
67 sint64 ldt;
68 TimeStringToMacLongDateTime(inUTCTime, ldt);
69 ioMacDate = uint32(ldt);
70 }
71
72 /*
73 * Given a CSSM_DATA containing either a UTC-style or "generalized time"
74 * time string, convert to 32-bit Mac time in seconds.
75 * Returns nonzero on error.
76 */
77 void
78 TimeStringToMacLongDateTime (const CSSM_DATA &inUTCTime, sint64 &outMacDate)
79 {
80 char szTemp[5];
81 size_t len;
82 int isUtc;
83 sint32 x;
84 sint32 i;
85 char *cp;
86
87 CFGregorianDate date;
88 ::memset( &date, 0, sizeof(date) );
89
90 if ((inUTCTime.Data == NULL) || (inUTCTime.Length == 0))
91 {
92 MacOSError::throwMe(errSecParam);
93 }
94
95 /* tolerate NULL terminated or not */
96 len = inUTCTime.Length;
97 if (inUTCTime.Data[len - 1] == '\0')
98 len--;
99
100 switch(len)
101 {
102 case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant
103 isUtc = 1;
104 break;
105 case GENERALIZED_TIME_STRLEN: // 4-digit year
106 isUtc = 0;
107 break;
108 default: // unknown format
109 MacOSError::throwMe(errSecParam);
110 }
111
112 cp = (char *)inUTCTime.Data;
113
114 /* check that all characters except last are digits */
115 for(i=0; i<(sint32)(len - 1); i++) {
116 if ( !(isdigit(cp[i])) ) {
117 MacOSError::throwMe(errSecParam);
118 }
119 }
120
121 /* check last character is a 'Z' */
122 if(cp[len - 1] != 'Z' ) {
123 MacOSError::throwMe(errSecParam);
124 }
125
126 /* YEAR */
127 szTemp[0] = *cp++;
128 szTemp[1] = *cp++;
129 if(!isUtc) {
130 /* two more digits */
131 szTemp[2] = *cp++;
132 szTemp[3] = *cp++;
133 szTemp[4] = '\0';
134 }
135 else {
136 szTemp[2] = '\0';
137 }
138 x = atoi( szTemp );
139 if(isUtc) {
140 /*
141 * 2-digit year.
142 * 0 <= year <= 50 : assume century 21
143 * 50 < year < 70 : illegal per PKIX
144 * 70 < year <= 99 : assume century 20
145 */
146 if(x <= 50) {
147 x += 100;
148 }
149 else if(x < 70) {
150 MacOSError::throwMe(errSecParam);
151 }
152 /* else century 20, OK */
153
154 /* bug fix... we need to end up with a 4-digit year! */
155 x += 1900;
156 }
157 /* by definition - tm_year is year - 1900 */
158 //tmp->tm_year = x - 1900;
159 date.year = x;
160
161 /* MONTH */
162 szTemp[0] = *cp++;
163 szTemp[1] = *cp++;
164 szTemp[2] = '\0';
165 x = atoi( szTemp );
166 /* in the string, months are from 1 to 12 */
167 if((x > 12) || (x <= 0)) {
168 MacOSError::throwMe(errSecParam);
169 }
170 /* in a tm, 0 to 11 */
171 //tmp->tm_mon = x - 1;
172 date.month = x;
173
174 /* DAY */
175 szTemp[0] = *cp++;
176 szTemp[1] = *cp++;
177 szTemp[2] = '\0';
178 x = atoi( szTemp );
179 /* 1..31 in both formats */
180 if((x > 31) || (x <= 0)) {
181 MacOSError::throwMe(errSecParam);
182 }
183 //tmp->tm_mday = x;
184 date.day = x;
185
186 /* HOUR */
187 szTemp[0] = *cp++;
188 szTemp[1] = *cp++;
189 szTemp[2] = '\0';
190 x = atoi( szTemp );
191 if((x > 23) || (x < 0)) {
192 MacOSError::throwMe(errSecParam);
193 }
194 //tmp->tm_hour = x;
195 date.hour = x;
196
197 /* MINUTE */
198 szTemp[0] = *cp++;
199 szTemp[1] = *cp++;
200 szTemp[2] = '\0';
201 x = atoi( szTemp );
202 if((x > 59) || (x < 0)) {
203 MacOSError::throwMe(errSecParam);
204 }
205 //tmp->tm_min = x;
206 date.minute = x;
207
208 /* SECOND */
209 szTemp[0] = *cp++;
210 szTemp[1] = *cp++;
211 szTemp[2] = '\0';
212 x = atoi( szTemp );
213 if((x > 59) || (x < 0)) {
214 MacOSError::throwMe(errSecParam);
215 }
216 //tmp->tm_sec = x;
217 date.second = x;
218
219 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
220 CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
221 CFRelease(timeZone);
222
223 // Adjust abstime to local timezone
224 timeZone = CFTimeZoneCopyDefault();
225 absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
226 CFRelease(timeZone);
227
228 outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
229 }
230
231 void MacSecondsToTimeString(uint32 inMacDate, uint32 inLength, void *outData)
232 {
233 sint64 ldt = sint64(uint64(inMacDate));
234 MacLongDateTimeToTimeString(ldt, inLength, outData);
235 }
236
237 void MacLongDateTimeToTimeString(const sint64 &inMacDate,
238 uint32 inLength, void *outData)
239 {
240 // @@@ this code is close, but on the fringe case of a daylight savings time it will be off for a little while
241 CFAbsoluteTime absTime = inMacDate - kCFAbsoluteTimeIntervalSince1904;
242
243 // Remove local timezone component from absTime
244 CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
245 absTime -= CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
246 CFRelease(timeZone);
247
248 timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
249 CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absTime, timeZone);
250 CFRelease(timeZone);
251
252 if (inLength == 16)
253 {
254 sprintf((char *)(outData), "%04d%02d%02d%02d%02d%02dZ",
255 int(date.year % 10000), date.month, date.day,
256 date.hour, date.minute, int(date.second));
257 }
258 else if (inLength == 14)
259 {
260 /* UTC - 2 year digits - code which parses this assumes that
261 * (2-digit) years between 0 and 49 are in century 21 */
262 sprintf((char *)(outData), "%02d%02d%02d%02d%02d%02dZ",
263 int(date.year % 100), date.month, date.day,
264 date.hour, date.minute, int(date.second));
265 }
266 else
267 MacOSError::throwMe(errSecParam);
268 }
269
270 void
271 CFDateToCssmDate(CFDateRef date, char *outCssmDate)
272 {
273 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
274 CFGregorianDate gd = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(date), timeZone);
275 sprintf(outCssmDate, "%04d%02d%02d%02d%02d%02dZ", (int)gd.year, gd.month, gd.day, gd.hour, gd.minute, (unsigned int)gd.second);
276 CFRelease(timeZone);
277 }
278
279 void
280 CssmDateToCFDate(const char *cssmDate, CFDateRef *outCFDate)
281 {
282 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
283 CFGregorianDate gd;
284 unsigned int year, month, day, hour, minute, second;
285 sscanf(cssmDate, "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second);
286 gd.year = year;
287 gd.month = month;
288 gd.day = day;
289 gd.hour = hour;
290 gd.minute = minute;
291 gd.second = second;
292 *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
293 CFRelease(timeZone);
294 }
295
296 int
297 CssmDateStringToCFDate(const char *cssmDate, unsigned int len, CFDateRef *outCFDate)
298 {
299 CFTimeZoneRef timeZone;
300 CFGregorianDate gd;
301 CFTimeInterval ti=0;
302 char szTemp[5];
303 unsigned isUtc=0, isLocal=0, i;
304 int x;
305 unsigned noSeconds=0;
306 char *cp;
307
308 if((cssmDate == NULL) || (len == 0) || (outCFDate == NULL))
309 return 1;
310
311 /* tolerate NULL terminated or not */
312 if(cssmDate[len - 1] == '\0')
313 len--;
314
315 switch(len) {
316 case UTC_TIME_NOSEC_LEN: // 2-digit year, no seconds, not y2K compliant
317 isUtc = 1;
318 noSeconds = 1;
319 break;
320 case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant
321 isUtc = 1;
322 break;
323 case GENERALIZED_TIME_STRLEN: // 4-digit year
324 //isUtc = 0;
325 break;
326 case LOCALIZED_UTC_TIME_STRLEN: // "YYMMDDhhmmssThhmm" (where T=[+,-])
327 isUtc = 1;
328 // deliberate fallthrough
329 case LOCALIZED_TIME_STRLEN: // "YYYYMMDDhhmmssThhmm" (where T=[+,-])
330 isLocal = 1;
331 break;
332 default: // unknown format
333 return 1;
334 }
335
336 cp = (char *)cssmDate;
337
338 /* check that all characters except last (or timezone indicator, if localized) are digits */
339 for(i=0; i<(len - 1); i++) {
340 if ( !(isdigit(cp[i])) )
341 if ( !isLocal || !(cp[i]=='+' || cp[i]=='-') )
342 return 1;
343 }
344 /* check last character is a 'Z', unless localized */
345 if(!isLocal && cp[len - 1] != 'Z' ) {
346 return 1;
347 }
348
349 /* YEAR */
350 szTemp[0] = *cp++;
351 szTemp[1] = *cp++;
352 if(!isUtc) {
353 /* two more digits */
354 szTemp[2] = *cp++;
355 szTemp[3] = *cp++;
356 szTemp[4] = '\0';
357 }
358 else {
359 szTemp[2] = '\0';
360 }
361 x = atoi( szTemp );
362 if(isUtc) {
363 /*
364 * 2-digit year.
365 * 0 <= year < 50 : assume century 21
366 * 50 <= year < 70 : illegal per PKIX
367 * 70 < year <= 99 : assume century 20
368 */
369 if(x < 50) {
370 x += 2000;
371 }
372 else if(x < 70) {
373 return 1;
374 }
375 else {
376 /* century 20 */
377 x += 1900;
378 }
379 }
380 gd.year = x;
381
382 /* MONTH */
383 szTemp[0] = *cp++;
384 szTemp[1] = *cp++;
385 szTemp[2] = '\0';
386 x = atoi( szTemp );
387 /* in the string, months are from 1 to 12 */
388 if((x > 12) || (x <= 0)) {
389 return 1;
390 }
391 gd.month = x;
392
393 /* DAY */
394 szTemp[0] = *cp++;
395 szTemp[1] = *cp++;
396 szTemp[2] = '\0';
397 x = atoi( szTemp );
398 /* 1..31 in both formats */
399 if((x > 31) || (x <= 0)) {
400 return 1;
401 }
402 gd.day = x;
403
404 /* HOUR */
405 szTemp[0] = *cp++;
406 szTemp[1] = *cp++;
407 szTemp[2] = '\0';
408 x = atoi( szTemp );
409 if((x > 23) || (x < 0)) {
410 return 1;
411 }
412 gd.hour = x;
413
414 /* MINUTE */
415 szTemp[0] = *cp++;
416 szTemp[1] = *cp++;
417 szTemp[2] = '\0';
418 x = atoi( szTemp );
419 if((x > 59) || (x < 0)) {
420 return 1;
421 }
422 gd.minute = x;
423
424 /* SECOND */
425 if(noSeconds) {
426 gd.second = 0;
427 }
428 else {
429 szTemp[0] = *cp++;
430 szTemp[1] = *cp++;
431 szTemp[2] = '\0';
432 x = atoi( szTemp );
433 if((x > 59) || (x < 0)) {
434 return 1;
435 }
436 gd.second = x;
437 }
438
439 if (isLocal) {
440 /* ZONE INDICATOR */
441 ti = (*cp++ == '+') ? 1 : -1;
442 /* ZONE HH OFFSET */
443 szTemp[0] = *cp++;
444 szTemp[1] = *cp++;
445 szTemp[2] = '\0';
446 x = atoi( szTemp ) * 60 * 60;
447 ti *= x;
448 /* ZONE MM OFFSET */
449 szTemp[0] = *cp++;
450 szTemp[1] = *cp++;
451 szTemp[2] = '\0';
452 x = atoi( szTemp ) * 60;
453 ti += ((ti < 0) ? (x*-1) : x);
454 }
455 timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, ti);
456 if (!timeZone) return 1;
457 *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
458 CFRelease(timeZone);
459
460 return 0;
461 }
462
463 }; // end namespace CSSMDateTimeUtils
464
465 } // end namespace Security