]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_cdsa_utils/lib/cuTimeStr.cpp
Security-57740.31.2.tar.gz
[apple/security.git] / OSX / libsecurity_cdsa_utils / lib / cuTimeStr.cpp
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 */
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 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 */
232 char *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