1/* 2 * Copyright (c) 2002,2011-2012,2014 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 * cuTimeStr.cpp - time string routines 20 */ 21#include "cuTimeStr.h" 22#include "cuCdsaUtils.h" 23#include <string.h> 24#include <stdlib.h> 25#include <stdio.h> 26#include <ctype.h> 27#include <pthread.h> 28 29/* 30 * Given a string containing either a UTC-style or "generalized time" 31 * time string, convert to a struct tm (in GMT/UTC). Returns nonzero on 32 * error. 33 */ 34int cuTimeStringToTm( 35 const char *str, 36 unsigned len, 37 struct tm *tmp) 38{ 39 char szTemp[5]; 40 unsigned isUtc = 0; 41 unsigned noSeconds = 0; 42 int x; 43 unsigned i; 44 char *cp; 45 46 if((str == NULL) || (len == 0) || (tmp == NULL)) { 47 return 1; 48 } 49 50 /* tolerate NULL terminated or not */ 51 if(str[len - 1] == '\0') { 52 len--; 53 } 54 switch(len) { 55 case UTC_TIME_NOSEC_LEN: // 2-digit year, no seconds, not y2K compliant 56 isUtc = 1; 57 noSeconds = 1; 58 break; 59 case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant 60 isUtc = 1; 61 break; 62 case GENERALIZED_TIME_STRLEN: // 4-digit year 63 break; 64 default: // unknown format 65 return 1; 66 } 67 68 cp = (char *)str; 69 70 /* check that all characters except last are digits */ 71 for(i=0; i<(len - 1); i++) { 72 if ( !(isdigit(cp[i])) ) { 73 return 1; 74 } 75 } 76 77 /* check last character is a 'Z' */ 78 if(cp[len - 1] != 'Z' ) { 79 return 1; 80 } 81 82 /* YEAR */ 83 szTemp[0] = *cp++; 84 szTemp[1] = *cp++; 85 if(!isUtc) { 86 /* two more digits */ 87 szTemp[2] = *cp++; 88 szTemp[3] = *cp++; 89 szTemp[4] = '\0'; 90 } 91 else { 92 szTemp[2] = '\0'; 93 } 94 x = atoi( szTemp ); 95 if(isUtc) { 96 /* 97 * 2-digit year. 98 * 0 <= year < 50 : assume century 21 99 * 50 <= year < 70 : illegal per PKIX, though we tolerate 100 * 70 < year <= 99 : assume century 20 101 */ 102 if(x < 50) { 103 x += 2000; 104 } 105 /* 106 else if(x < 70) { 107 return 1; 108 } 109 */ 110 else { 111 /* century 20 */ 112 x += 1900; 113 } 114 } 115 /* by definition - tm_year is year - 1900 */ 116 tmp->tm_year = x - 1900; 117 118 /* MONTH */ 119 szTemp[0] = *cp++; 120 szTemp[1] = *cp++; 121 szTemp[2] = '\0'; 122 x = atoi( szTemp ); 123 /* in the string, months are from 1 to 12 */ 124 if((x > 12) || (x <= 0)) { 125 return 1; 126 } 127 /* in a tm, 0 to 11 */ 128 tmp->tm_mon = x - 1; 129 130 /* DAY */ 131 szTemp[0] = *cp++; 132 szTemp[1] = *cp++; 133 szTemp[2] = '\0'; 134 x = atoi( szTemp ); 135 /* 1..31 in both formats */ 136 if((x > 31) || (x <= 0)) { 137 return 1; 138 } 139 tmp->tm_mday = x; 140 141 /* HOUR */ 142 szTemp[0] = *cp++; 143 szTemp[1] = *cp++; 144 szTemp[2] = '\0'; 145 x = atoi( szTemp ); 146 if((x > 23) || (x < 0)) { 147 return 1; 148 } 149 tmp->tm_hour = x; 150 151 /* MINUTE */ 152 szTemp[0] = *cp++; 153 szTemp[1] = *cp++; 154 szTemp[2] = '\0'; 155 x = atoi( szTemp ); 156 if((x > 59) || (x < 0)) { 157 return 1; 158 } 159 tmp->tm_min = x; 160 161 /* SECOND */ 162 if(noSeconds) { 163 tmp->tm_sec = 0; 164 } 165 else { 166 szTemp[0] = *cp++; 167 szTemp[1] = *cp++; 168 szTemp[2] = '\0'; 169 x = atoi( szTemp ); 170 if((x > 59) || (x < 0)) { 171 return 1; 172 } 173 tmp->tm_sec = x; 174 } 175 return 0; 176} 177 178#define MAX_TIME_STR_LEN 30 179 180/* protects time(), gmtime() */ 181static pthread_mutex_t timeMutex = PTHREAD_MUTEX_INITIALIZER; 182 183char *cuTimeAtNowPlus(int secFromNow, 184 timeSpec spec) 185{ 186 struct tm utc; 187 char *outStr; 188 time_t baseTime; 189 190 pthread_mutex_lock(&timeMutex); 191 baseTime = time(NULL); 192 baseTime += (time_t)secFromNow; 193 utc = *gmtime(&baseTime); 194 pthread_mutex_unlock(&timeMutex); 195 196 outStr = (char *)APP_MALLOC(MAX_TIME_STR_LEN); 197 198 switch(spec) { 199 case TIME_UTC: 200 /* UTC - 2 year digits - code which parses this assumes that 201 * (2-digit) years between 0 and 49 are in century 21 */ 202 if(utc.tm_year >= 100) { 203 utc.tm_year -= 100; 204 } 205 sprintf(outStr, "%02d%02d%02d%02d%02d%02dZ", 206 utc.tm_year /* + 1900 */, utc.tm_mon + 1, 207 utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec); 208 break; 209 case TIME_GEN: 210 sprintf(outStr, "%04d%02d%02d%02d%02d%02dZ", 211 /* note year is relative to 1900, hopefully it'll 212 * have four valid digits! */ 213 utc.tm_year + 1900, utc.tm_mon + 1, 214 utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec); 215 break; 216 case TIME_CSSM: 217 sprintf(outStr, "%04d%02d%02d%02d%02d%02d", 218 /* note year is relative to 1900, hopefully it'll have 219 * four valid digits! */ 220 utc.tm_year + 1900, utc.tm_mon + 1, 221 utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec); 222 break; 223 } 224 return outStr; 225} 226 227/* 228 * Convert a CSSM_X509_TIME, which can be in any of three forms (UTC, 229 * generalized, or CSSM_TIMESTRING) into a CSSM_TIMESTRING. Caller 230 * must free() the result. Returns NULL if x509time is badly formed. 231 */ 232char *cuX509TimeToCssmTimestring( 233 const CSSM_X509_TIME *x509Time, 234 unsigned *rtnLen) // for caller's convenience 235{ 236 int len = (int)x509Time->time.Length; 237 const char *inStr = (char *)x509Time->time.Data; 238 // not NULL terminated! 239 char *rtn; 240 241 *rtnLen = 0; 242 if((len == 0) || (inStr == NULL)) { 243 return NULL; 244 } 245 rtn = (char *)malloc(CSSM_TIME_STRLEN + 1); 246 rtn[0] = '\0'; 247 switch(len) { 248 case UTC_TIME_STRLEN: 249 { 250 /* infer century and prepend to output */ 251 char tmp[3]; 252 int year; 253 tmp[0] = inStr[0]; 254 tmp[1] = inStr[1]; 255 tmp[2] = '\0'; 256 year = atoi(tmp); 257 258 /* 259 * 0 <= year < 50 : assume century 21 260 * 50 <= year < 70 : illegal per PKIX 261 * 70 < year <= 99 : assume century 20 262 */ 263 if(year < 50) { 264 /* century 21 */ 265 strcpy(rtn, "20"); 266 } 267 else if(year < 70) { 268 free(rtn); 269 return NULL; 270 } 271 else { 272 /* century 20 */ 273 strcpy(rtn, "19"); 274 } 275 memmove(rtn + 2, inStr, len - 1); // don't copy the Z 276 break; 277 } 278 case CSSM_TIME_STRLEN: 279 memmove(rtn, inStr, len); // trivial case 280 break; 281 case GENERALIZED_TIME_STRLEN: 282 memmove(rtn, inStr, len - 1); // don't copy the Z 283 break; 284 285 default: 286 free(rtn); 287 return NULL; 288 } 289 rtn[CSSM_TIME_STRLEN] = '\0'; 290 *rtnLen = CSSM_TIME_STRLEN; 291 return rtn; 292} 293 294