blob: 9e8375e5c80161dea4461cb2c8732b4ba7694411 [file] [log] [blame]
vlmfa67ddc2004-06-03 03:38:44 +00001/*-
2 * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
3 * Redistribution and modifications are permitted subject to BSD license.
4 */
5#include <GeneralizedTime.h>
6#include <time.h>
7#include <errno.h>
8#ifndef __NO_ASSERT_H__
9#include <assert.h>
10#endif /* __NO_ASSERT_H__ */
11
vlm6e73a042004-08-11 07:17:22 +000012#ifdef WIN32
13#define localtime_r(tlocp, tmp) (*tmp = localtime(&tlocp))
14#warning PLEASE STOP AND READ!
15#warning localtime_r is implemented via localtime(), which is not thread-safe. You must fix the code to insert appropriate locking if you want to use asn_GT2time() or asn_UT2time().
16#warning PLEASE STOP AND READ!
17#endif
18
vlmfa67ddc2004-06-03 03:38:44 +000019#ifndef __NO_ASN_TABLE__
20
21/*
22 * GeneralizedTime basic type description.
23 */
24static ber_tlv_tag_t asn1_DEF_GeneralizedTime_tags[] = {
25 (ASN_TAG_CLASS_UNIVERSAL | (24 << 2))
26};
27asn1_TYPE_descriptor_t asn1_DEF_GeneralizedTime = {
28 "GeneralizedTime",
29 GeneralizedTime_constraint, /* Check validity of time */
30 OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */
vlm81057a82004-08-07 03:52:26 +000031 GeneralizedTime_encode_der, /* Implemented in terms of OCTET STRING */
vlmfa67ddc2004-06-03 03:38:44 +000032 GeneralizedTime_print,
33 OCTET_STRING_free,
34 0, /* Use generic outmost tag fetcher */
35 asn1_DEF_GeneralizedTime_tags,
36 sizeof(asn1_DEF_GeneralizedTime_tags)
37 / sizeof(asn1_DEF_GeneralizedTime_tags[0]),
38 1, /* Single UNIVERSAL tag may be implicitly overriden */
39 -1, /* Both ways are fine */
vlmb42843a2004-06-05 08:17:50 +000040 0 /* No specifics */
vlmfa67ddc2004-06-03 03:38:44 +000041};
42
43#endif /* __NO_ASN_TABLE__ */
44
45/*
46 * Check that the time looks like the time.
47 */
48int
49GeneralizedTime_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,
50 asn_app_consume_bytes_f *app_errlog, void *app_key) {
51 const GeneralizedTime_t *st = sptr;
52 time_t tloc;
53
54 errno = EPERM; /* Just an unlikely error code */
vlm81057a82004-08-07 03:52:26 +000055 tloc = asn_GT2time(st, 0, 0);
vlmfa67ddc2004-06-03 03:38:44 +000056 if(tloc == -1 && errno != EPERM) {
57 _ASN_ERRLOG("%s: Invalid time format: %s",
58 td->name, strerror(errno));
59 return -1;
60 }
61
62 return 0;
63}
64
vlm81057a82004-08-07 03:52:26 +000065der_enc_rval_t
66GeneralizedTime_encode_der(asn1_TYPE_descriptor_t *td, void *ptr,
67 int tag_mode, ber_tlv_tag_t tag,
68 asn_app_consume_bytes_f *cb, void *app_key) {
69 GeneralizedTime_t *st = ptr;
70 der_enc_rval_t erval;
71
72 /* If not canonical DER, re-encode into canonical DER. */
73 if(st->size && st->buf[st->size-1] != 'Z') {
74 struct tm tm;
75 time_t tloc;
76
77 errno = EPERM;
78 tloc = asn_GT2time(st, &tm, 1); /* Recognize time */
79 if(tloc == -1 && errno != EPERM) {
80 /* Failed to recognize time. Fail completely. */
81 erval.encoded = -1;
82 erval.failed_type = td;
83 erval.structure_ptr = ptr;
84 return erval;
85 }
86 st = asn_time2GT(0, &tm, 1); /* Save time canonically */
87 if(!st) {
88 /* Memory allocation failure. */
89 erval.encoded = -1;
90 erval.failed_type = td;
91 erval.structure_ptr = ptr;
92 return erval;
93 }
94 }
95
96 erval = OCTET_STRING_encode_der(td, st, tag_mode, tag, cb, app_key);
97
98 if(st != ptr) {
99 FREEMEM(st->buf);
100 FREEMEM(st);
101 }
102
103 return erval;
104}
105
vlmfa67ddc2004-06-03 03:38:44 +0000106int
107GeneralizedTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
108 asn_app_consume_bytes_f *cb, void *app_key) {
109 const GeneralizedTime_t *st = sptr;
110
vlmb42843a2004-06-05 08:17:50 +0000111 (void)td; /* Unused argument */
112 (void)ilevel; /* Unused argument */
113
vlmfa67ddc2004-06-03 03:38:44 +0000114 if(st && st->buf) {
115 char buf[32];
116 struct tm tm;
117 int ret;
118
119 errno = EPERM;
vlm81057a82004-08-07 03:52:26 +0000120 if(asn_GT2time(st, &tm, 1) == -1 && errno != EPERM)
vlmfa67ddc2004-06-03 03:38:44 +0000121 return cb("<bad-value>", 11, app_key);
122
123 ret = snprintf(buf, sizeof(buf),
vlm81057a82004-08-07 03:52:26 +0000124 "%04d-%02d-%02d %02d:%02d%02d (GMT)",
vlmfa67ddc2004-06-03 03:38:44 +0000125 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
126 tm.tm_hour, tm.tm_min, tm.tm_sec);
vlmb42843a2004-06-05 08:17:50 +0000127 assert(ret > 0 && ret < (int)sizeof(buf));
vlmfa67ddc2004-06-03 03:38:44 +0000128 return cb(buf, ret, app_key);
129 } else {
130 return cb("<absent>", 8, app_key);
131 }
132}
133
134/*
135 * Where to look for offset from GMT, Phase I.
136 * Several platforms are known.
137 */
138#if defined(__FreeBSD__) || (defined(__GNUC__) && defined(__APPLE_CC__))
139#undef HAVE_TM_ZONE
140#define HAVE_TM_ZONE
141#endif /* BSDs */
142
143/*
144 * Where to look for offset from GMT, Phase II.
145 */
146#ifdef HAVE_TM_ZONE
vlm81057a82004-08-07 03:52:26 +0000147#define GMTOFF(tm) ((tm).tm_gmtoff)
vlmfa67ddc2004-06-03 03:38:44 +0000148#else /* HAVE_TM_ZONE */
vlm81057a82004-08-07 03:52:26 +0000149#define GMTOFF(tm) (-timezone)
vlmfa67ddc2004-06-03 03:38:44 +0000150#endif /* HAVE_TM_ZONE */
151
152time_t
vlm81057a82004-08-07 03:52:26 +0000153asn_GT2time(const GeneralizedTime_t *st, struct tm *ret_tm, int as_gmt) {
vlmfa67ddc2004-06-03 03:38:44 +0000154 struct tm tm_s;
155 uint8_t *buf;
156 uint8_t *end;
vlm81057a82004-08-07 03:52:26 +0000157 int gmtoff_h = 0;
158 int gmtoff_m = 0;
159 int gmtoff = 0; /* h + m */
vlmfa67ddc2004-06-03 03:38:44 +0000160 int offset_specified = 0;
161 time_t tloc;
162
163 if(!st || !st->buf) {
164 errno = EINVAL;
165 return -1;
166 } else {
167 buf = st->buf;
168 end = buf + st->size;
169 }
170
171 if(st->size < 10) {
172 errno = EINVAL;
173 return -1;
174 }
175
176 /*
177 * Decode first 10 bytes: "AAAAMMJJhh"
178 */
179 memset(&tm_s, 0, sizeof(tm_s));
180#undef B2F
181#undef B2T
182#define B2F(var) do { \
183 unsigned ch = *buf; \
184 if(ch < 0x30 && ch > 0x39) { \
185 errno = EINVAL; \
186 return -1; \
187 } else { \
188 var = var * 10 + (ch - 0x30); \
189 buf++; \
190 } \
191 } while(0)
192#define B2T(var) B2F(tm_s.var)
193
194 B2T(tm_year); /* 1: A */
195 B2T(tm_year); /* 2: A */
196 B2T(tm_year); /* 3: A */
197 B2T(tm_year); /* 4: A */
198 B2T(tm_mon); /* 5: M */
199 B2T(tm_mon); /* 6: M */
200 B2T(tm_mday); /* 7: J */
201 B2T(tm_mday); /* 8: J */
202 B2T(tm_hour); /* 9: h */
203 B2T(tm_hour); /* 0: h */
204
205 if(buf == end) goto local_finish;
206
207 /*
208 * Parse [mm[ss[(.|,)ffff]]]
209 * ^^
210 */
211 switch(*buf) {
212 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
213 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
214 tm_s.tm_min = (*buf++) - 0x30;
215 if(buf == end) { errno = EINVAL; return -1; }
216 B2T(tm_min);
217 break;
218 case 0x2B: case 0x2D: /* +, - */
219 goto offset;
220 case 0x5A: /* Z */
221 goto utc_finish;
222 default:
223 errno = EINVAL;
224 return -1;
225 }
226
227 if(buf == end) goto local_finish;
228
229 /*
230 * Parse [mm[ss[(.|,)ffff]]]
231 * ^^
232 */
233 switch(*buf) {
234 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
235 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
236 tm_s.tm_sec = (*buf++) - 0x30;
237 if(buf == end) { errno = EINVAL; return -1; }
238 B2T(tm_sec);
239 break;
240 case 0x2B: case 0x2D: /* +, - */
241 goto offset;
242 case 0x5A: /* Z */
243 goto utc_finish;
244 default:
245 errno = EINVAL;
246 return -1;
247 }
248
249 if(buf == end) goto local_finish;
250
251 /*
252 * Parse [mm[ss[(.|,)ffff]]]
253 * ^ ^
254 */
255 switch(*buf) {
256 case 0x2C: case 0x2E: /* (.|,) */
257 /* Fractions of seconds are not supported
258 * by time_t or struct tm. Skip them */
259 for(buf++; buf < end; buf++) {
260 switch(*buf) {
261 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
262 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
263 continue;
264 default:
265 break;
266 }
267 break;
268 }
269 }
270
271 if(buf == end) goto local_finish;
272
273 switch(*buf) {
274 case 0x2B: case 0x2D: /* +, - */
275 goto offset;
276 case 0x5A: /* Z */
277 goto utc_finish;
278 default:
279 errno = EINVAL;
280 return -1;
281 }
282
283
284offset:
285
286 if(end - buf < 3) {
287 errno = EINVAL;
288 return -1;
289 }
290 buf++;
vlm81057a82004-08-07 03:52:26 +0000291 B2F(gmtoff_h);
292 B2F(gmtoff_h);
vlmfa67ddc2004-06-03 03:38:44 +0000293 if(buf[-3] == 0x2D) /* Negative */
vlm81057a82004-08-07 03:52:26 +0000294 gmtoff = -1;
vlmfa67ddc2004-06-03 03:38:44 +0000295 else
vlm81057a82004-08-07 03:52:26 +0000296 gmtoff = 1;
vlmfa67ddc2004-06-03 03:38:44 +0000297
298 if((end - buf) == 2) {
vlm81057a82004-08-07 03:52:26 +0000299 B2F(gmtoff_m);
300 B2F(gmtoff_m);
vlmfa67ddc2004-06-03 03:38:44 +0000301 } else if(end != buf) {
302 errno = EINVAL;
303 return -1;
304 }
305
vlm81057a82004-08-07 03:52:26 +0000306 gmtoff = gmtoff * (3600 * gmtoff_h + 60 * gmtoff_m);
vlmfa67ddc2004-06-03 03:38:44 +0000307
308 /* Fall through */
309utc_finish:
310
311 offset_specified = 1;
312
313 /* Fall through */
314local_finish:
315
316 /*
317 * Validation.
318 */
319 if((tm_s.tm_mon > 12 || tm_s.tm_mon < 1)
320 || (tm_s.tm_mday > 31 || tm_s.tm_mday < 1)
321 || (tm_s.tm_hour > 23)
322 || (tm_s.tm_sec > 60)
323 ) {
324 errno = EINVAL;
325 return -1;
326 }
327
328 /* Canonicalize */
329 tm_s.tm_mon -= 1; /* 0 - 11 */
330 tm_s.tm_year -= 1900;
331 tm_s.tm_isdst = -1;
332
vlm81057a82004-08-07 03:52:26 +0000333 tm_s.tm_sec -= gmtoff;
334
335 /*** AT THIS POINT tm_s is either GMT or local (unknown) ****/
336
337 if(offset_specified)
338 tloc = timegm(&tm_s);
339 else {
340 /*
341 * Without an offset (or 'Z'),
342 * we can only guess that it is a local zone.
343 * Interpret it in this fashion.
344 */
345 tloc = mktime(&tm_s);
346 }
vlmfa67ddc2004-06-03 03:38:44 +0000347 if(tloc == -1) {
348 errno = EINVAL;
349 return -1;
350 }
351
vlm81057a82004-08-07 03:52:26 +0000352 if(ret_tm) {
353 if(as_gmt) {
354 if(offset_specified) {
355 *ret_tm = tm_s;
356 } else {
357 if(gmtime_r(&tloc, ret_tm) == 0) {
358 errno = EINVAL;
359 return -1;
360 }
361 }
362 } else {
363 if(localtime_r(&tloc, ret_tm) == 0) {
364 errno = EINVAL;
365 return -1;
366 }
vlmfa67ddc2004-06-03 03:38:44 +0000367 }
368 }
369
vlmfa67ddc2004-06-03 03:38:44 +0000370 return tloc;
371}
372
vlm81057a82004-08-07 03:52:26 +0000373
374GeneralizedTime_t *
375asn_time2GT(GeneralizedTime_t *opt_gt, const struct tm *tm, int force_gmt) {
376 struct tm tm_s;
377 long gmtoff;
378 const unsigned int buf_size = 24; /* 4+2+2 +2+2+2 +4 + cushion */
379 char *buf;
380 char *p;
381 int size;
382
383 /* Check arguments */
384 if(!tm) {
385 errno = EINVAL;
386 return 0;
387 }
388
389 /* Pre-allocate a buffer of sufficient yet small length */
390 buf = MALLOC(buf_size);
391 if(!buf) return 0;
392
393 gmtoff = GMTOFF(*tm);
394
395 if(force_gmt && gmtoff) {
396 tm_s = *tm;
397 tm_s.tm_sec -= gmtoff;
398 timegm(&tm_s); /* Fix the time */
399 assert(!GMTOFF(tm_s));
400 tm = &tm_s;
401 }
402
403 size = snprintf(buf, buf_size, "%04d%02d%02d%02d%02d%02d",
404 tm->tm_year + 1900,
405 tm->tm_mon + 1,
406 tm->tm_mday,
407 tm->tm_hour,
408 tm->tm_min,
409 tm->tm_sec
410 );
411 assert(size == 14);
412
413 p = buf + size;
414 if(force_gmt) {
415 *p++ = 0x5a; /* 'Z' */
416 *p++ = 0;
417 size++;
418 } else {
419 int ret = snprintf(p, buf_size - size, "%+03ld%02ld",
420 gmtoff / 3600, gmtoff % 3600);
421 assert(ret >= 5 && ret <= 7);
422 size += ret;
423 }
424
425 if(opt_gt) {
426 if(opt_gt->buf)
427 FREEMEM(opt_gt->buf);
428 } else {
429 opt_gt = CALLOC(1, sizeof *opt_gt);
430 if(!opt_gt) { free(buf); return 0; }
431 }
432
433 opt_gt->buf = buf;
434 opt_gt->size = size;
435
436 return opt_gt;
437}
438
439