blob: 1dadb92def92eae040ae8334214285ff13a97fa1 [file] [log] [blame]
Lev Walkinf15320b2004-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
12#ifndef __NO_ASN_TABLE__
13
14/*
15 * GeneralizedTime basic type description.
16 */
17static ber_tlv_tag_t asn1_DEF_GeneralizedTime_tags[] = {
18 (ASN_TAG_CLASS_UNIVERSAL | (24 << 2))
19};
20asn1_TYPE_descriptor_t asn1_DEF_GeneralizedTime = {
21 "GeneralizedTime",
22 GeneralizedTime_constraint, /* Check validity of time */
23 OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */
24 OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */
25 GeneralizedTime_print,
26 OCTET_STRING_free,
27 0, /* Use generic outmost tag fetcher */
28 asn1_DEF_GeneralizedTime_tags,
29 sizeof(asn1_DEF_GeneralizedTime_tags)
30 / sizeof(asn1_DEF_GeneralizedTime_tags[0]),
31 1, /* Single UNIVERSAL tag may be implicitly overriden */
32 -1, /* Both ways are fine */
Lev Walkind9bd7752004-06-05 08:17:50 +000033 0 /* No specifics */
Lev Walkinf15320b2004-06-03 03:38:44 +000034};
35
36#endif /* __NO_ASN_TABLE__ */
37
38/*
39 * Check that the time looks like the time.
40 */
41int
42GeneralizedTime_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,
43 asn_app_consume_bytes_f *app_errlog, void *app_key) {
44 const GeneralizedTime_t *st = sptr;
45 time_t tloc;
46
47 errno = EPERM; /* Just an unlikely error code */
48 tloc = asn_GT2time(st, 0);
49 if(tloc == -1 && errno != EPERM) {
50 _ASN_ERRLOG("%s: Invalid time format: %s",
51 td->name, strerror(errno));
52 return -1;
53 }
54
55 return 0;
56}
57
58int
59GeneralizedTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
60 asn_app_consume_bytes_f *cb, void *app_key) {
61 const GeneralizedTime_t *st = sptr;
62
Lev Walkind9bd7752004-06-05 08:17:50 +000063 (void)td; /* Unused argument */
64 (void)ilevel; /* Unused argument */
65
Lev Walkinf15320b2004-06-03 03:38:44 +000066 if(st && st->buf) {
67 char buf[32];
68 struct tm tm;
69 int ret;
70
71 errno = EPERM;
72 if(asn_GT2time(st, &tm) == -1 && errno != EPERM)
73 return cb("<bad-value>", 11, app_key);
74
75 ret = snprintf(buf, sizeof(buf),
76 "%04d-%02d-%02d %02d:%02d%02d",
77 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
78 tm.tm_hour, tm.tm_min, tm.tm_sec);
Lev Walkind9bd7752004-06-05 08:17:50 +000079 assert(ret > 0 && ret < (int)sizeof(buf));
Lev Walkinf15320b2004-06-03 03:38:44 +000080 return cb(buf, ret, app_key);
81 } else {
82 return cb("<absent>", 8, app_key);
83 }
84}
85
86/*
87 * Where to look for offset from GMT, Phase I.
88 * Several platforms are known.
89 */
90#if defined(__FreeBSD__) || (defined(__GNUC__) && defined(__APPLE_CC__))
91#undef HAVE_TM_ZONE
92#define HAVE_TM_ZONE
93#endif /* BSDs */
94
95/*
96 * Where to look for offset from GMT, Phase II.
97 */
98#ifdef HAVE_TM_ZONE
99#define GMTOFF (tm_s.tm_gmtoff)
100#else /* HAVE_TM_ZONE */
101#define GMTOFF (-timezone)
102#endif /* HAVE_TM_ZONE */
103
104time_t
105asn_GT2time(const GeneralizedTime_t *st, struct tm *_tm) {
106 struct tm tm_s;
107 uint8_t *buf;
108 uint8_t *end;
109 int tm_gmtoff_h = 0;
110 int tm_gmtoff_m = 0;
111 int tm_gmtoff = 0; /* h + m */
112 int offset_specified = 0;
113 time_t tloc;
114
115 if(!st || !st->buf) {
116 errno = EINVAL;
117 return -1;
118 } else {
119 buf = st->buf;
120 end = buf + st->size;
121 }
122
123 if(st->size < 10) {
124 errno = EINVAL;
125 return -1;
126 }
127
128 /*
129 * Decode first 10 bytes: "AAAAMMJJhh"
130 */
131 memset(&tm_s, 0, sizeof(tm_s));
132#undef B2F
133#undef B2T
134#define B2F(var) do { \
135 unsigned ch = *buf; \
136 if(ch < 0x30 && ch > 0x39) { \
137 errno = EINVAL; \
138 return -1; \
139 } else { \
140 var = var * 10 + (ch - 0x30); \
141 buf++; \
142 } \
143 } while(0)
144#define B2T(var) B2F(tm_s.var)
145
146 B2T(tm_year); /* 1: A */
147 B2T(tm_year); /* 2: A */
148 B2T(tm_year); /* 3: A */
149 B2T(tm_year); /* 4: A */
150 B2T(tm_mon); /* 5: M */
151 B2T(tm_mon); /* 6: M */
152 B2T(tm_mday); /* 7: J */
153 B2T(tm_mday); /* 8: J */
154 B2T(tm_hour); /* 9: h */
155 B2T(tm_hour); /* 0: h */
156
157 if(buf == end) goto local_finish;
158
159 /*
160 * Parse [mm[ss[(.|,)ffff]]]
161 * ^^
162 */
163 switch(*buf) {
164 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
165 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
166 tm_s.tm_min = (*buf++) - 0x30;
167 if(buf == end) { errno = EINVAL; return -1; }
168 B2T(tm_min);
169 break;
170 case 0x2B: case 0x2D: /* +, - */
171 goto offset;
172 case 0x5A: /* Z */
173 goto utc_finish;
174 default:
175 errno = EINVAL;
176 return -1;
177 }
178
179 if(buf == end) goto local_finish;
180
181 /*
182 * Parse [mm[ss[(.|,)ffff]]]
183 * ^^
184 */
185 switch(*buf) {
186 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
187 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
188 tm_s.tm_sec = (*buf++) - 0x30;
189 if(buf == end) { errno = EINVAL; return -1; }
190 B2T(tm_sec);
191 break;
192 case 0x2B: case 0x2D: /* +, - */
193 goto offset;
194 case 0x5A: /* Z */
195 goto utc_finish;
196 default:
197 errno = EINVAL;
198 return -1;
199 }
200
201 if(buf == end) goto local_finish;
202
203 /*
204 * Parse [mm[ss[(.|,)ffff]]]
205 * ^ ^
206 */
207 switch(*buf) {
208 case 0x2C: case 0x2E: /* (.|,) */
209 /* Fractions of seconds are not supported
210 * by time_t or struct tm. Skip them */
211 for(buf++; buf < end; buf++) {
212 switch(*buf) {
213 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
214 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
215 continue;
216 default:
217 break;
218 }
219 break;
220 }
221 }
222
223 if(buf == end) goto local_finish;
224
225 switch(*buf) {
226 case 0x2B: case 0x2D: /* +, - */
227 goto offset;
228 case 0x5A: /* Z */
229 goto utc_finish;
230 default:
231 errno = EINVAL;
232 return -1;
233 }
234
235
236offset:
237
238 if(end - buf < 3) {
239 errno = EINVAL;
240 return -1;
241 }
242 buf++;
243 B2F(tm_gmtoff_h);
244 B2F(tm_gmtoff_h);
245 if(buf[-3] == 0x2D) /* Negative */
246 tm_gmtoff = -1;
247 else
248 tm_gmtoff = 1;
249
250 if((end - buf) == 2) {
251 B2F(tm_gmtoff_m);
252 B2F(tm_gmtoff_m);
253 } else if(end != buf) {
254 errno = EINVAL;
255 return -1;
256 }
257
258 tm_gmtoff = tm_gmtoff * (3600 * tm_gmtoff_h + 60 * tm_gmtoff_m);
259
260 /* Fall through */
261utc_finish:
262
263 offset_specified = 1;
264
265 /* Fall through */
266local_finish:
267
268 /*
269 * Validation.
270 */
271 if((tm_s.tm_mon > 12 || tm_s.tm_mon < 1)
272 || (tm_s.tm_mday > 31 || tm_s.tm_mday < 1)
273 || (tm_s.tm_hour > 23)
274 || (tm_s.tm_sec > 60)
275 ) {
276 errno = EINVAL;
277 return -1;
278 }
279
280 /* Canonicalize */
281 tm_s.tm_mon -= 1; /* 0 - 11 */
282 tm_s.tm_year -= 1900;
283 tm_s.tm_isdst = -1;
284
285 tloc = mktime(&tm_s);
286 if(tloc == -1) {
287 errno = EINVAL;
288 return -1;
289 }
290
291 if(offset_specified) {
292 /*
293 * Offset from GMT is specified in the time expression.
294 */
295 tloc += GMTOFF - tm_gmtoff;
296 if(_tm && (localtime_r(&tloc, &tm_s) == NULL)) {
297 /* Could not reconstruct the time */
298 return -1;
299 }
300 }
301
302 if(_tm) memcpy(_tm, &tm_s, sizeof(struct tm));
303
304 return tloc;
305}
306