]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/cssmdatetime.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_keychain / lib / cssmdatetime.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, 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 <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
36 #include <CoreFoundation/CFDate.h>
37 #include <CoreFoundation/CFTimeZone.h>
38 #include <ctype.h>
39 #include <stdlib.h>
40
41 namespace Security
42 {
43
44 namespace CSSMDateTimeUtils
45 {
46
47 #define UTC_TIME_NOSEC_LEN 11
48 #define UTC_TIME_STRLEN 13
49 #define GENERALIZED_TIME_STRLEN 15
50 #define LOCALIZED_UTC_TIME_STRLEN 17
51 #define LOCALIZED_TIME_STRLEN 19
52 #define MAX_TIME_STR_LEN 30
53
54
55 void
56 GetCurrentMacLongDateTime(sint64 &outMacDate)
57 {
58 CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
59 CFAbsoluteTime absTime = CFAbsoluteTimeGetCurrent();
60 absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
61 CFRelease(timeZone);
62 outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
63 }
64
65 void
66 TimeStringToMacSeconds (const CSSM_DATA &inUTCTime, uint32 &ioMacDate)
67 {
68 sint64 ldt;
69 TimeStringToMacLongDateTime(inUTCTime, ldt);
70 ioMacDate = uint32(ldt);
71 }
72
73 /*
74 * Given a CSSM_DATA containing either a UTC-style or "generalized time"
75 * time string, convert to 32-bit Mac time in seconds.
76 * Returns nonzero on error.
77 */
78 void
79 TimeStringToMacLongDateTime (const CSSM_DATA &inUTCTime, sint64 &outMacDate)
80 {
81 char szTemp[5];
82 unsigned len;
83 int isUtc;
84 sint32 x;
85 sint32 i;
86 char *cp;
87
88 CFGregorianDate date;
89 ::memset( &date, 0, sizeof(date) );
90
91 if ((inUTCTime.Data == NULL) || (inUTCTime.Length == 0))
92 {
93 MacOSError::throwMe(paramErr);
94 }
95
96 /* tolerate NULL terminated or not */
97 len = inUTCTime.Length;
98 if (inUTCTime.Data[len - 1] == '\0')
99 len--;
100
101 switch(len)
102 {
103 case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant
104 isUtc = 1;
105 break;
106 case GENERALIZED_TIME_STRLEN: // 4-digit year
107 isUtc = 0;
108 break;
109 default: // unknown format
110 MacOSError::throwMe(paramErr);
111 }
112
113 cp = (char *)inUTCTime.Data;
114
115 /* check that all characters except last are digits */
116 for(i=0; i<(sint32)(len - 1); i++) {
117 if ( !(isdigit(cp[i])) ) {
118 MacOSError::throwMe(paramErr);
119 }
120 }
121
122 /* check last character is a 'Z' */
123 if(cp[len - 1] != 'Z' ) {
124 MacOSError::throwMe(paramErr);
125 }
126
127 /* YEAR */
128 szTemp[0] = *cp++;
129 szTemp[1] = *cp++;
130 if(!isUtc) {
131 /* two more digits */
132 szTemp[2] = *cp++;
133 szTemp[3] = *cp++;
134 szTemp[4] = '\0';
135 }
136 else {
137 szTemp[2] = '\0';
138 }
139 x = atoi( szTemp );
140 if(isUtc) {
141 /*
142 * 2-digit year.
143 * 0 <= year <= 50 : assume century 21
144 * 50 < year < 70 : illegal per PKIX
145 * 70 < year <= 99 : assume century 20
146 */
147 if(x <= 50) {
148 x += 100;
149 }
150 else if(x < 70) {
151 MacOSError::throwMe(paramErr);
152 }
153 /* else century 20, OK */
154
155 /* bug fix... we need to end up with a 4-digit year! */
156 x += 1900;
157 }
158 /* by definition - tm_year is year - 1900 */
159 //tmp->tm_year = x - 1900;
160 date.year = x;
161
162 /* MONTH */
163 szTemp[0] = *cp++;
164 szTemp[1] = *cp++;
165 szTemp[2] = '\0';
166 x = atoi( szTemp );
167 /* in the string, months are from 1 to 12 */
168 if((x > 12) || (x <= 0)) {
169 MacOSError::throwMe(paramErr);
170 }
171 /* in a tm, 0 to 11 */
172 //tmp->tm_mon = x - 1;
173 date.month = x;
174
175 /* DAY */
176 szTemp[0] = *cp++;
177 szTemp[1] = *cp++;
178 szTemp[2] = '\0';
179 x = atoi( szTemp );
180 /* 1..31 in both formats */
181 if((x > 31) || (x <= 0)) {
182 MacOSError::throwMe(paramErr);
183 }
184 //tmp->tm_mday = x;
185 date.day = x;
186
187 /* HOUR */
188 szTemp[0] = *cp++;
189 szTemp[1] = *cp++;
190 szTemp[2] = '\0';
191 x = atoi( szTemp );
192 if((x > 23) || (x < 0)) {
193 MacOSError::throwMe(paramErr);
194 }
195 //tmp->tm_hour = x;
196 date.hour = x;
197
198 /* MINUTE */
199 szTemp[0] = *cp++;
200 szTemp[1] = *cp++;
201 szTemp[2] = '\0';
202 x = atoi( szTemp );
203 if((x > 59) || (x < 0)) {
204 MacOSError::throwMe(paramErr);
205 }
206 //tmp->tm_min = x;
207 date.minute = x;
208
209 /* SECOND */
210 szTemp[0] = *cp++;
211 szTemp[1] = *cp++;
212 szTemp[2] = '\0';
213 x = atoi( szTemp );
214 if((x > 59) || (x < 0)) {
215 MacOSError::throwMe(paramErr);
216 }
217 //tmp->tm_sec = x;
218 date.second = x;
219
220 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
221 CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
222 CFRelease(timeZone);
223
224 // Adjust abstime to local timezone
225 timeZone = CFTimeZoneCopyDefault();
226 absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
227 CFRelease(timeZone);
228
229 outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
230 }
231
232 void MacSecondsToTimeString(uint32 inMacDate, uint32 inLength, void *outData)
233 {
234 sint64 ldt = sint64(uint64(inMacDate));
235 MacLongDateTimeToTimeString(ldt, inLength, outData);
236 }
237
238 void MacLongDateTimeToTimeString(const sint64 &inMacDate,
239 uint32 inLength, void *outData)
240 {
241 // @@@ this code is close, but on the fringe case of a daylight savings time it will be off for a little while
242 CFAbsoluteTime absTime = inMacDate - kCFAbsoluteTimeIntervalSince1904;
243
244 // Remove local timezone component from absTime
245 CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
246 absTime -= CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
247 CFRelease(timeZone);
248
249 timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
250 CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absTime, timeZone);
251 CFRelease(timeZone);
252
253 if (inLength == 16)
254 {
255 sprintf((char *)(outData), "%04d%02d%02d%02d%02d%02dZ",
256 int(date.year % 10000), date.month, date.day,
257 date.hour, date.minute, int(date.second));
258 }
259 else if (inLength == 14)
260 {
261 /* UTC - 2 year digits - code which parses this assumes that
262 * (2-digit) years between 0 and 49 are in century 21 */
263 sprintf((char *)(outData), "%02d%02d%02d%02d%02d%02dZ",
264 int(date.year % 100), date.month, date.day,
265 date.hour, date.minute, int(date.second));
266 }
267 else
268 MacOSError::throwMe(paramErr);
269 }
270
271 void
272 CFDateToCssmDate(CFDateRef date, char *outCssmDate)
273 {
274 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
275 CFGregorianDate gd = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(date), timeZone);
276 sprintf(outCssmDate, "%04d%02d%02d%02d%02d%02dZ", (int)gd.year, gd.month, gd.day, gd.hour, gd.minute, (unsigned int)gd.second);
277 CFRelease(timeZone);
278 }
279
280 void
281 CssmDateToCFDate(const char *cssmDate, CFDateRef *outCFDate)
282 {
283 CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
284 CFGregorianDate gd;
285 unsigned int year, month, day, hour, minute, second;
286 sscanf(cssmDate, "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second);
287 gd.year = year;
288 gd.month = month;
289 gd.day = day;
290 gd.hour = hour;
291 gd.minute = minute;
292 gd.second = second;
293 *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
294 CFRelease(timeZone);
295 }
296
297 int
298 CssmDateStringToCFDate(const char *cssmDate, unsigned int len, CFDateRef *outCFDate)
299 {
300 CFTimeZoneRef timeZone;
301 CFGregorianDate gd;
302 CFTimeInterval ti=0;
303 char szTemp[5];
304 unsigned isUtc=0, isLocal=0, x, i;
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