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