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