]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_keychain/lib/cssmdatetime.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / cssmdatetime.cpp
diff --git a/Security/libsecurity_keychain/lib/cssmdatetime.cpp b/Security/libsecurity_keychain/lib/cssmdatetime.cpp
new file mode 100644 (file)
index 0000000..dd0378e
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ * cssmdatetime.cpp -- CSSM date and time utilities for the Mac
+ */
+
+#ifdef __MWERKS__
+#define _CPP_CSSM_DATE_TIME_UTILS
+#endif
+
+#include "cssmdatetime.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <security_utilities/errors.h>
+#include <CoreFoundation/CFDate.h>
+#include <CoreFoundation/CFTimeZone.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <SecBase.h>
+namespace Security
+{
+
+namespace CSSMDateTimeUtils
+{
+
+#define UTC_TIME_NOSEC_LEN                     11
+#define UTC_TIME_STRLEN                                13
+#define GENERALIZED_TIME_STRLEN                15
+#define LOCALIZED_UTC_TIME_STRLEN      17
+#define LOCALIZED_TIME_STRLEN          19
+#define MAX_TIME_STR_LEN                       30
+
+
+void
+GetCurrentMacLongDateTime(sint64 &outMacDate)
+{
+       CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
+       CFAbsoluteTime absTime = CFAbsoluteTimeGetCurrent();
+       absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
+       CFRelease(timeZone);
+       outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
+}
+
+void
+TimeStringToMacSeconds (const CSSM_DATA &inUTCTime, uint32 &ioMacDate)
+{
+    sint64 ldt;
+    TimeStringToMacLongDateTime(inUTCTime, ldt);
+    ioMacDate = uint32(ldt);
+}
+
+/*
+ * Given a CSSM_DATA containing either a UTC-style or "generalized time"
+ * time string, convert to 32-bit Mac time in seconds.
+ * Returns nonzero on error. 
+ */
+void
+TimeStringToMacLongDateTime (const CSSM_DATA &inUTCTime, sint64 &outMacDate)
+{
+       char            szTemp[5];
+       size_t          len;
+       int             isUtc;
+       sint32          x;
+       sint32          i;
+       char            *cp;
+
+       CFGregorianDate date;
+       ::memset( &date, 0, sizeof(date) );
+
+       if ((inUTCTime.Data == NULL) || (inUTCTime.Length == 0))
+    {
+       MacOSError::throwMe(errSecParam);
+       }
+
+       /* tolerate NULL terminated or not */
+       len = inUTCTime.Length;
+       if (inUTCTime.Data[len - 1] == '\0')
+               len--;
+
+       switch(len)
+    {
+               case UTC_TIME_STRLEN:                   // 2-digit year, not Y2K compliant
+                       isUtc = 1;
+                       break;
+               case GENERALIZED_TIME_STRLEN:   // 4-digit year
+                       isUtc = 0;
+                       break;
+               default:                                                // unknown format 
+            MacOSError::throwMe(errSecParam);
+       }
+
+       cp = (char *)inUTCTime.Data;
+       
+       /* check that all characters except last are digits */
+    for(i=0; i<(sint32)(len - 1); i++) {
+               if ( !(isdigit(cp[i])) ) {
+            MacOSError::throwMe(errSecParam);
+               }
+       }
+
+       /* check last character is a 'Z' */
+       if(cp[len - 1] != 'Z' ) {
+        MacOSError::throwMe(errSecParam);
+       }
+
+       /* YEAR */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       if(!isUtc) {
+               /* two more digits */
+               szTemp[2] = *cp++;
+               szTemp[3] = *cp++;
+               szTemp[4] = '\0';
+       }
+       else { 
+               szTemp[2] = '\0';
+       }
+       x = atoi( szTemp );
+       if(isUtc) {
+               /* 
+                * 2-digit year. 
+                *   0  <= year <= 50 : assume century 21
+                *   50 <  year <  70 : illegal per PKIX
+                *   70 <  year <= 99 : assume century 20
+                */
+               if(x <= 50) {
+                       x += 100;
+               }
+               else if(x < 70) {
+            MacOSError::throwMe(errSecParam);
+               }
+               /* else century 20, OK */
+
+               /* bug fix... we need to end up with a 4-digit year! */
+               x += 1900;
+       }
+       /* by definition - tm_year is year - 1900 */
+       //tmp->tm_year = x - 1900;
+       date.year = x;
+
+       /* MONTH */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       /* in the string, months are from 1 to 12 */
+       if((x > 12) || (x <= 0)) {
+        MacOSError::throwMe(errSecParam);
+       }
+       /* in a tm, 0 to 11 */
+       //tmp->tm_mon = x - 1;
+       date.month = x;
+
+       /* DAY */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       /* 1..31 in both formats */
+       if((x > 31) || (x <= 0)) {
+        MacOSError::throwMe(errSecParam);
+       }
+       //tmp->tm_mday = x;
+       date.day = x;
+
+       /* HOUR */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       if((x > 23) || (x < 0)) {
+        MacOSError::throwMe(errSecParam);
+       }
+       //tmp->tm_hour = x;
+       date.hour = x;
+
+       /* MINUTE */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       if((x > 59) || (x < 0)) {
+        MacOSError::throwMe(errSecParam);
+       }
+       //tmp->tm_min = x;
+       date.minute = x;
+
+       /* SECOND */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       if((x > 59) || (x < 0)) {
+        MacOSError::throwMe(errSecParam);
+       }
+       //tmp->tm_sec = x;
+       date.second = x;
+
+       CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
+       CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
+       CFRelease(timeZone);
+
+       // Adjust abstime to local timezone
+       timeZone = CFTimeZoneCopyDefault();
+       absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
+       CFRelease(timeZone);
+
+       outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
+}
+
+void MacSecondsToTimeString(uint32 inMacDate, uint32 inLength, void *outData)
+{
+    sint64 ldt = sint64(uint64(inMacDate));
+    MacLongDateTimeToTimeString(ldt, inLength, outData);
+}
+
+void MacLongDateTimeToTimeString(const sint64 &inMacDate,
+                                        uint32 inLength, void *outData)
+{
+       // @@@ this code is close, but on the fringe case of a daylight savings time it will be off for a little while
+       CFAbsoluteTime absTime = inMacDate - kCFAbsoluteTimeIntervalSince1904;
+
+       // Remove local timezone component from absTime
+       CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
+       absTime -= CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
+       CFRelease(timeZone);
+
+       timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
+       CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absTime, timeZone);
+       CFRelease(timeZone);
+
+    if (inLength == 16)
+    {
+        sprintf((char *)(outData), "%04d%02d%02d%02d%02d%02dZ",
+                int(date.year % 10000), date.month, date.day,
+                date.hour, date.minute, int(date.second));
+    }
+    else if (inLength == 14)
+    {
+               /* UTC - 2 year digits - code which parses this assumes that
+                * (2-digit) years between 0 and 49 are in century 21 */
+        sprintf((char *)(outData), "%02d%02d%02d%02d%02d%02dZ",
+                int(date.year % 100), date.month, date.day,
+                date.hour, date.minute, int(date.second));
+    }
+    else
+        MacOSError::throwMe(errSecParam);
+}
+
+void
+CFDateToCssmDate(CFDateRef date, char *outCssmDate)
+{
+       CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
+       CFGregorianDate gd = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(date), timeZone);
+       sprintf(outCssmDate, "%04d%02d%02d%02d%02d%02dZ", (int)gd.year, gd.month, gd.day, gd.hour, gd.minute, (unsigned int)gd.second);
+       CFRelease(timeZone);
+}
+
+void
+CssmDateToCFDate(const char *cssmDate, CFDateRef *outCFDate)
+{
+       CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
+       CFGregorianDate gd;
+       unsigned int year, month, day, hour, minute, second;
+       sscanf(cssmDate, "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second);
+       gd.year = year;
+       gd.month = month;
+       gd.day = day;
+       gd.hour = hour;
+       gd.minute = minute;
+       gd.second = second;
+       *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
+       CFRelease(timeZone);
+}
+
+int
+CssmDateStringToCFDate(const char *cssmDate, unsigned int len, CFDateRef *outCFDate)
+{
+       CFTimeZoneRef timeZone;
+       CFGregorianDate gd;
+       CFTimeInterval ti=0;
+       char szTemp[5];
+       unsigned isUtc=0, isLocal=0, i;
+        int x;
+       unsigned noSeconds=0;
+       char *cp;
+
+       if((cssmDate == NULL) || (len == 0) || (outCFDate == NULL))
+       return 1;
+       
+       /* tolerate NULL terminated or not */
+       if(cssmDate[len - 1] == '\0')
+               len--;
+
+       switch(len) {
+               case UTC_TIME_NOSEC_LEN:                // 2-digit year, no seconds, not y2K compliant
+                       isUtc = 1;
+                       noSeconds = 1;
+                       break;
+               case UTC_TIME_STRLEN:                   // 2-digit year, not Y2K compliant
+                       isUtc = 1;
+                       break;
+               case GENERALIZED_TIME_STRLEN:   // 4-digit year
+                       //isUtc = 0;
+                       break;
+               case LOCALIZED_UTC_TIME_STRLEN: // "YYMMDDhhmmssThhmm" (where T=[+,-])
+                       isUtc = 1;
+                       // deliberate fallthrough
+               case LOCALIZED_TIME_STRLEN:             // "YYYYMMDDhhmmssThhmm" (where T=[+,-])
+                       isLocal = 1;
+                       break;
+               default:                                                // unknown format 
+                       return 1;
+       }
+       
+       cp = (char *)cssmDate;
+       
+       /* check that all characters except last (or timezone indicator, if localized) are digits */
+       for(i=0; i<(len - 1); i++) {
+               if ( !(isdigit(cp[i])) )
+                       if ( !isLocal || !(cp[i]=='+' || cp[i]=='-') )
+                               return 1;
+       }
+       /* check last character is a 'Z', unless localized */
+       if(!isLocal && cp[len - 1] != 'Z' )     {
+               return 1;
+       }
+
+       /* YEAR */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       if(!isUtc) {
+               /* two more digits */
+               szTemp[2] = *cp++;
+               szTemp[3] = *cp++;
+               szTemp[4] = '\0';
+       }
+       else { 
+               szTemp[2] = '\0';
+       }
+       x = atoi( szTemp );
+       if(isUtc) {
+               /* 
+                * 2-digit year. 
+                *   0  <= year <  50 : assume century 21
+                *   50 <= year <  70 : illegal per PKIX
+                *   70 <  year <= 99 : assume century 20
+                */
+               if(x < 50) {
+                       x += 2000;
+               }
+               else if(x < 70) {
+                       return 1;
+               }
+               else {
+                       /* century 20 */
+                       x += 1900;                      
+               }
+       }
+       gd.year = x;
+
+       /* MONTH */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       /* in the string, months are from 1 to 12 */
+       if((x > 12) || (x <= 0)) {
+       return 1;
+       }
+       gd.month = x;
+
+       /* DAY */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       /* 1..31 in both formats */
+       if((x > 31) || (x <= 0)) {
+               return 1;
+       }
+       gd.day = x;
+
+       /* HOUR */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       if((x > 23) || (x < 0)) {
+               return 1;
+       }
+       gd.hour = x;
+
+       /* MINUTE */
+       szTemp[0] = *cp++;
+       szTemp[1] = *cp++;
+       szTemp[2] = '\0';
+       x = atoi( szTemp );
+       if((x > 59) || (x < 0)) {
+               return 1;
+       }
+       gd.minute = x;
+
+       /* SECOND */
+       if(noSeconds) {
+               gd.second = 0;
+       }
+       else {
+               szTemp[0] = *cp++;
+               szTemp[1] = *cp++;
+               szTemp[2] = '\0';
+               x = atoi( szTemp );
+               if((x > 59) || (x < 0)) {
+                       return 1;
+               }
+               gd.second = x;
+       }
+       
+       if (isLocal) {
+               /* ZONE INDICATOR */
+               ti = (*cp++ == '+') ? 1 : -1;
+               /* ZONE HH OFFSET */
+               szTemp[0] = *cp++;
+               szTemp[1] = *cp++;
+               szTemp[2] = '\0';
+               x = atoi( szTemp ) * 60 * 60;
+               ti *= x;
+               /* ZONE MM OFFSET */
+               szTemp[0] = *cp++;
+               szTemp[1] = *cp++;
+               szTemp[2] = '\0';
+               x = atoi( szTemp ) * 60;
+               ti += ((ti < 0) ? (x*-1) : x);
+       }
+       timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, ti);
+       if (!timeZone) return 1;
+       *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
+       CFRelease(timeZone);
+       
+       return 0;
+}
+
+}; // end namespace CSSMDateTimeUtils
+
+} // end namespace Security