]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_cdsa_utils/lib/cuTimeStr.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_cdsa_utils / lib / cuTimeStr.cpp
1 /*
2 * Copyright (c) 2002,2011-2012,2014-2019 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 */
34 int 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() */
181 static pthread_mutex_t timeMutex = PTHREAD_MUTEX_INITIALIZER;
182
183 char *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 /* Check for legacy value of 1 */
199 if (spec == CU_TIME_LEGACY) {
200 #if (TIME_UTC == 1)
201 /* time.h has defined TIME_UTC=1, so legacy caller
202 actually wants TIME_UTC, not TIME_CSSM. */
203 spec = CU_TIME_UTC;
204 #else
205 spec = CU_TIME_CSSM;
206 #endif
207 }
208
209 switch(spec) {
210 case CU_TIME_UTC:
211 case CU_TIME_LEGACY:
212 /* UTC - 2 year digits - code which parses this assumes that
213 * (2-digit) years between 0 and 49 are in century 21 */
214 if(utc.tm_year >= 100) {
215 utc.tm_year -= 100;
216 }
217 sprintf(outStr, "%02d%02d%02d%02d%02d%02dZ",
218 utc.tm_year /* + 1900 */, utc.tm_mon + 1,
219 utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
220 break;
221 case CU_TIME_GEN:
222 sprintf(outStr, "%04d%02d%02d%02d%02d%02dZ",
223 /* note year is relative to 1900, hopefully it'll
224 * have four valid digits! */
225 utc.tm_year + 1900, utc.tm_mon + 1,
226 utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
227 break;
228 case CU_TIME_CSSM:
229 sprintf(outStr, "%04d%02d%02d%02d%02d%02d",
230 /* note year is relative to 1900, hopefully it'll have
231 * four valid digits! */
232 utc.tm_year + 1900, utc.tm_mon + 1,
233 utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
234 break;
235 }
236 return outStr;
237 }
238
239 /*
240 * Convert a CSSM_X509_TIME, which can be in any of three forms (UTC,
241 * generalized, or CSSM_TIMESTRING) into a CSSM_TIMESTRING. Caller
242 * must free() the result. Returns NULL if x509time is badly formed.
243 */
244 char *cuX509TimeToCssmTimestring(
245 const CSSM_X509_TIME *x509Time,
246 unsigned *rtnLen) // for caller's convenience
247 {
248 int len = (int)x509Time->time.Length;
249 const char *inStr = (char *)x509Time->time.Data;
250 // not NULL terminated!
251 char *rtn;
252
253 *rtnLen = 0;
254 if((len == 0) || (inStr == NULL)) {
255 return NULL;
256 }
257 rtn = (char *)malloc(CSSM_TIME_STRLEN + 1);
258 rtn[0] = '\0';
259 switch(len) {
260 case UTC_TIME_STRLEN:
261 {
262 /* infer century and prepend to output */
263 char tmp[3];
264 int year;
265 tmp[0] = inStr[0];
266 tmp[1] = inStr[1];
267 tmp[2] = '\0';
268 year = atoi(tmp);
269
270 /*
271 * 0 <= year < 50 : assume century 21
272 * 50 <= year < 70 : illegal per PKIX
273 * 70 < year <= 99 : assume century 20
274 */
275 if(year < 50) {
276 /* century 21 */
277 strcpy(rtn, "20");
278 }
279 else if(year < 70) {
280 free(rtn);
281 return NULL;
282 }
283 else {
284 /* century 20 */
285 strcpy(rtn, "19");
286 }
287 memmove(rtn + 2, inStr, len - 1); // don't copy the Z
288 break;
289 }
290 case CSSM_TIME_STRLEN:
291 memmove(rtn, inStr, len); // trivial case
292 break;
293 case GENERALIZED_TIME_STRLEN:
294 memmove(rtn, inStr, len - 1); // don't copy the Z
295 break;
296
297 default:
298 free(rtn);
299 return NULL;
300 }
301 rtn[CSSM_TIME_STRLEN] = '\0';
302 *rtnLen = CSSM_TIME_STRLEN;
303 return rtn;
304 }
305