2004-06-03 03:38:44 +00:00
/*-
* Copyright ( c ) 2003 , 2004 Lev Walkin < vlm @ lionet . info > . All rights reserved .
* Redistribution and modifications are permitted subject to BSD license .
*/
2004-08-11 07:35:08 +00:00
# define __USE_BSD /* To enable tm_gmtoff in glibc */
2004-06-03 03:38:44 +00:00
# include <GeneralizedTime.h>
# include <time.h>
# include <errno.h>
# ifndef __NO_ASSERT_H__
# include <assert.h>
# endif /* __NO_ASSERT_H__ */
2004-08-11 07:17:22 +00:00
# ifdef WIN32
# define localtime_r(tlocp, tmp) (*tmp = localtime(&tlocp))
# warning PLEASE STOP AND READ!
# 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().
# warning PLEASE STOP AND READ!
# endif
2004-06-03 03:38:44 +00:00
# ifndef __NO_ASN_TABLE__
/*
* GeneralizedTime basic type description .
*/
static ber_tlv_tag_t asn1_DEF_GeneralizedTime_tags [ ] = {
( ASN_TAG_CLASS_UNIVERSAL | ( 24 < < 2 ) )
} ;
asn1_TYPE_descriptor_t asn1_DEF_GeneralizedTime = {
" GeneralizedTime " ,
GeneralizedTime_constraint , /* Check validity of time */
OCTET_STRING_decode_ber , /* Implemented in terms of OCTET STRING */
2004-08-07 03:52:26 +00:00
GeneralizedTime_encode_der , /* Implemented in terms of OCTET STRING */
2004-06-03 03:38:44 +00:00
GeneralizedTime_print ,
OCTET_STRING_free ,
0 , /* Use generic outmost tag fetcher */
asn1_DEF_GeneralizedTime_tags ,
sizeof ( asn1_DEF_GeneralizedTime_tags )
/ sizeof ( asn1_DEF_GeneralizedTime_tags [ 0 ] ) ,
1 , /* Single UNIVERSAL tag may be implicitly overriden */
- 1 , /* Both ways are fine */
2004-06-05 08:17:50 +00:00
0 /* No specifics */
2004-06-03 03:38:44 +00:00
} ;
# endif /* __NO_ASN_TABLE__ */
/*
* Check that the time looks like the time .
*/
int
GeneralizedTime_constraint ( asn1_TYPE_descriptor_t * td , const void * sptr ,
asn_app_consume_bytes_f * app_errlog , void * app_key ) {
const GeneralizedTime_t * st = sptr ;
time_t tloc ;
errno = EPERM ; /* Just an unlikely error code */
2004-08-07 03:52:26 +00:00
tloc = asn_GT2time ( st , 0 , 0 ) ;
2004-06-03 03:38:44 +00:00
if ( tloc = = - 1 & & errno ! = EPERM ) {
_ASN_ERRLOG ( " %s: Invalid time format: %s " ,
td - > name , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
2004-08-07 03:52:26 +00:00
der_enc_rval_t
GeneralizedTime_encode_der ( asn1_TYPE_descriptor_t * td , void * ptr ,
int tag_mode , ber_tlv_tag_t tag ,
asn_app_consume_bytes_f * cb , void * app_key ) {
GeneralizedTime_t * st = ptr ;
der_enc_rval_t erval ;
/* If not canonical DER, re-encode into canonical DER. */
if ( st - > size & & st - > buf [ st - > size - 1 ] ! = ' Z ' ) {
struct tm tm ;
time_t tloc ;
errno = EPERM ;
tloc = asn_GT2time ( st , & tm , 1 ) ; /* Recognize time */
if ( tloc = = - 1 & & errno ! = EPERM ) {
/* Failed to recognize time. Fail completely. */
erval . encoded = - 1 ;
erval . failed_type = td ;
erval . structure_ptr = ptr ;
return erval ;
}
st = asn_time2GT ( 0 , & tm , 1 ) ; /* Save time canonically */
if ( ! st ) {
/* Memory allocation failure. */
erval . encoded = - 1 ;
erval . failed_type = td ;
erval . structure_ptr = ptr ;
return erval ;
}
}
erval = OCTET_STRING_encode_der ( td , st , tag_mode , tag , cb , app_key ) ;
if ( st ! = ptr ) {
FREEMEM ( st - > buf ) ;
FREEMEM ( st ) ;
}
return erval ;
}
2004-06-03 03:38:44 +00:00
int
GeneralizedTime_print ( asn1_TYPE_descriptor_t * td , const void * sptr , int ilevel ,
asn_app_consume_bytes_f * cb , void * app_key ) {
const GeneralizedTime_t * st = sptr ;
2004-06-05 08:17:50 +00:00
( void ) td ; /* Unused argument */
( void ) ilevel ; /* Unused argument */
2004-06-03 03:38:44 +00:00
if ( st & & st - > buf ) {
char buf [ 32 ] ;
struct tm tm ;
int ret ;
errno = EPERM ;
2004-08-07 03:52:26 +00:00
if ( asn_GT2time ( st , & tm , 1 ) = = - 1 & & errno ! = EPERM )
2004-06-03 03:38:44 +00:00
return cb ( " <bad-value> " , 11 , app_key ) ;
ret = snprintf ( buf , sizeof ( buf ) ,
2004-08-07 03:52:26 +00:00
" %04d-%02d-%02d %02d:%02d%02d (GMT) " ,
2004-06-03 03:38:44 +00:00
tm . tm_year + 1900 , tm . tm_mon + 1 , tm . tm_mday ,
tm . tm_hour , tm . tm_min , tm . tm_sec ) ;
2004-06-05 08:17:50 +00:00
assert ( ret > 0 & & ret < ( int ) sizeof ( buf ) ) ;
2004-06-03 03:38:44 +00:00
return cb ( buf , ret , app_key ) ;
} else {
return cb ( " <absent> " , 8 , app_key ) ;
}
}
/*
* Where to look for offset from GMT , Phase I .
* Several platforms are known .
*/
2004-08-11 07:35:08 +00:00
# if defined(__FreeBSD__) \
| | ( defined ( __GNUC__ ) & & defined ( __APPLE_CC__ ) ) \
| | ( defined __GLIBC__ & & __GLIBC__ > = 2 )
# undef HAVE_TM_GMTOFF
# define HAVE_TM_GMTOFF
# endif /* BSDs and newer glibc */
2004-06-03 03:38:44 +00:00
/*
* Where to look for offset from GMT , Phase II .
*/
2004-08-11 07:35:08 +00:00
# ifdef HAVE_TM_GMTOFF
2004-08-07 03:52:26 +00:00
# define GMTOFF(tm) ((tm).tm_gmtoff)
2004-08-11 07:35:08 +00:00
# else /* HAVE_TM_GMTOFF */
2004-08-07 03:52:26 +00:00
# define GMTOFF(tm) (-timezone)
2004-08-11 07:35:08 +00:00
# endif /* HAVE_TM_GMTOFF */
2004-06-03 03:38:44 +00:00
time_t
2004-08-07 03:52:26 +00:00
asn_GT2time ( const GeneralizedTime_t * st , struct tm * ret_tm , int as_gmt ) {
2004-06-03 03:38:44 +00:00
struct tm tm_s ;
uint8_t * buf ;
uint8_t * end ;
2004-08-07 03:52:26 +00:00
int gmtoff_h = 0 ;
int gmtoff_m = 0 ;
int gmtoff = 0 ; /* h + m */
2004-06-03 03:38:44 +00:00
int offset_specified = 0 ;
time_t tloc ;
if ( ! st | | ! st - > buf ) {
errno = EINVAL ;
return - 1 ;
} else {
buf = st - > buf ;
end = buf + st - > size ;
}
if ( st - > size < 10 ) {
errno = EINVAL ;
return - 1 ;
}
/*
* Decode first 10 bytes : " AAAAMMJJhh "
*/
memset ( & tm_s , 0 , sizeof ( tm_s ) ) ;
# undef B2F
# undef B2T
# define B2F(var) do { \
unsigned ch = * buf ; \
if ( ch < 0x30 & & ch > 0x39 ) { \
errno = EINVAL ; \
return - 1 ; \
} else { \
var = var * 10 + ( ch - 0x30 ) ; \
buf + + ; \
} \
} while ( 0 )
# define B2T(var) B2F(tm_s.var)
B2T ( tm_year ) ; /* 1: A */
B2T ( tm_year ) ; /* 2: A */
B2T ( tm_year ) ; /* 3: A */
B2T ( tm_year ) ; /* 4: A */
B2T ( tm_mon ) ; /* 5: M */
B2T ( tm_mon ) ; /* 6: M */
B2T ( tm_mday ) ; /* 7: J */
B2T ( tm_mday ) ; /* 8: J */
B2T ( tm_hour ) ; /* 9: h */
B2T ( tm_hour ) ; /* 0: h */
if ( buf = = end ) goto local_finish ;
/*
* Parse [ mm [ ss [ ( . | , ) ffff ] ] ]
* ^ ^
*/
switch ( * buf ) {
case 0x30 : case 0x31 : case 0x32 : case 0x33 : case 0x34 :
case 0x35 : case 0x36 : case 0x37 : case 0x38 : case 0x39 :
tm_s . tm_min = ( * buf + + ) - 0x30 ;
if ( buf = = end ) { errno = EINVAL ; return - 1 ; }
B2T ( tm_min ) ;
break ;
case 0x2B : case 0x2D : /* +, - */
goto offset ;
case 0x5A : /* Z */
goto utc_finish ;
default :
errno = EINVAL ;
return - 1 ;
}
if ( buf = = end ) goto local_finish ;
/*
* Parse [ mm [ ss [ ( . | , ) ffff ] ] ]
* ^ ^
*/
switch ( * buf ) {
case 0x30 : case 0x31 : case 0x32 : case 0x33 : case 0x34 :
case 0x35 : case 0x36 : case 0x37 : case 0x38 : case 0x39 :
tm_s . tm_sec = ( * buf + + ) - 0x30 ;
if ( buf = = end ) { errno = EINVAL ; return - 1 ; }
B2T ( tm_sec ) ;
break ;
case 0x2B : case 0x2D : /* +, - */
goto offset ;
case 0x5A : /* Z */
goto utc_finish ;
default :
errno = EINVAL ;
return - 1 ;
}
if ( buf = = end ) goto local_finish ;
/*
* Parse [ mm [ ss [ ( . | , ) ffff ] ] ]
* ^ ^
*/
switch ( * buf ) {
case 0x2C : case 0x2E : /* (.|,) */
/* Fractions of seconds are not supported
* by time_t or struct tm . Skip them */
for ( buf + + ; buf < end ; buf + + ) {
switch ( * buf ) {
case 0x30 : case 0x31 : case 0x32 : case 0x33 : case 0x34 :
case 0x35 : case 0x36 : case 0x37 : case 0x38 : case 0x39 :
continue ;
default :
break ;
}
break ;
}
}
if ( buf = = end ) goto local_finish ;
switch ( * buf ) {
case 0x2B : case 0x2D : /* +, - */
goto offset ;
case 0x5A : /* Z */
goto utc_finish ;
default :
errno = EINVAL ;
return - 1 ;
}
offset :
if ( end - buf < 3 ) {
errno = EINVAL ;
return - 1 ;
}
buf + + ;
2004-08-07 03:52:26 +00:00
B2F ( gmtoff_h ) ;
B2F ( gmtoff_h ) ;
2004-06-03 03:38:44 +00:00
if ( buf [ - 3 ] = = 0x2D ) /* Negative */
2004-08-07 03:52:26 +00:00
gmtoff = - 1 ;
2004-06-03 03:38:44 +00:00
else
2004-08-07 03:52:26 +00:00
gmtoff = 1 ;
2004-06-03 03:38:44 +00:00
if ( ( end - buf ) = = 2 ) {
2004-08-07 03:52:26 +00:00
B2F ( gmtoff_m ) ;
B2F ( gmtoff_m ) ;
2004-06-03 03:38:44 +00:00
} else if ( end ! = buf ) {
errno = EINVAL ;
return - 1 ;
}
2004-08-07 03:52:26 +00:00
gmtoff = gmtoff * ( 3600 * gmtoff_h + 60 * gmtoff_m ) ;
2004-06-03 03:38:44 +00:00
/* Fall through */
utc_finish :
offset_specified = 1 ;
/* Fall through */
local_finish :
/*
* Validation .
*/
if ( ( tm_s . tm_mon > 12 | | tm_s . tm_mon < 1 )
| | ( tm_s . tm_mday > 31 | | tm_s . tm_mday < 1 )
| | ( tm_s . tm_hour > 23 )
| | ( tm_s . tm_sec > 60 )
) {
errno = EINVAL ;
return - 1 ;
}
/* Canonicalize */
tm_s . tm_mon - = 1 ; /* 0 - 11 */
tm_s . tm_year - = 1900 ;
tm_s . tm_isdst = - 1 ;
2004-08-07 03:52:26 +00:00
tm_s . tm_sec - = gmtoff ;
/*** AT THIS POINT tm_s is either GMT or local (unknown) ****/
if ( offset_specified )
tloc = timegm ( & tm_s ) ;
else {
/*
* Without an offset ( or ' Z ' ) ,
* we can only guess that it is a local zone .
* Interpret it in this fashion .
*/
tloc = mktime ( & tm_s ) ;
}
2004-06-03 03:38:44 +00:00
if ( tloc = = - 1 ) {
errno = EINVAL ;
return - 1 ;
}
2004-08-07 03:52:26 +00:00
if ( ret_tm ) {
if ( as_gmt ) {
if ( offset_specified ) {
* ret_tm = tm_s ;
} else {
if ( gmtime_r ( & tloc , ret_tm ) = = 0 ) {
errno = EINVAL ;
return - 1 ;
}
}
} else {
if ( localtime_r ( & tloc , ret_tm ) = = 0 ) {
errno = EINVAL ;
return - 1 ;
}
2004-06-03 03:38:44 +00:00
}
}
return tloc ;
}
2004-08-07 03:52:26 +00:00
GeneralizedTime_t *
asn_time2GT ( GeneralizedTime_t * opt_gt , const struct tm * tm , int force_gmt ) {
struct tm tm_s ;
long gmtoff ;
const unsigned int buf_size = 24 ; /* 4+2+2 +2+2+2 +4 + cushion */
char * buf ;
char * p ;
int size ;
/* Check arguments */
if ( ! tm ) {
errno = EINVAL ;
return 0 ;
}
/* Pre-allocate a buffer of sufficient yet small length */
buf = MALLOC ( buf_size ) ;
if ( ! buf ) return 0 ;
gmtoff = GMTOFF ( * tm ) ;
if ( force_gmt & & gmtoff ) {
tm_s = * tm ;
tm_s . tm_sec - = gmtoff ;
timegm ( & tm_s ) ; /* Fix the time */
tm = & tm_s ;
2004-08-11 07:35:08 +00:00
# ifdef HAVE_TM_GMTOFF
assert ( ! GMTOFF ( tm_s ) ) ; /* Will fix itself */
# else
gmtoff = 0 ; /* Intervention required */
# endif
2004-08-07 03:52:26 +00:00
}
size = snprintf ( buf , buf_size , " %04d%02d%02d%02d%02d%02d " ,
tm - > tm_year + 1900 ,
tm - > tm_mon + 1 ,
tm - > tm_mday ,
tm - > tm_hour ,
tm - > tm_min ,
tm - > tm_sec
) ;
assert ( size = = 14 ) ;
p = buf + size ;
if ( force_gmt ) {
* p + + = 0x5a ; /* 'Z' */
* p + + = 0 ;
size + + ;
} else {
int ret = snprintf ( p , buf_size - size , " %+03ld%02ld " ,
gmtoff / 3600 , gmtoff % 3600 ) ;
assert ( ret > = 5 & & ret < = 7 ) ;
size + = ret ;
}
if ( opt_gt ) {
if ( opt_gt - > buf )
FREEMEM ( opt_gt - > buf ) ;
} else {
opt_gt = CALLOC ( 1 , sizeof * opt_gt ) ;
if ( ! opt_gt ) { free ( buf ) ; return 0 ; }
}
2004-08-11 08:10:13 +00:00
opt_gt - > buf = ( unsigned char * ) buf ;
2004-08-07 03:52:26 +00:00
opt_gt - > size = size ;
return opt_gt ;
}