xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/os/gmt_mktime.c (revision afdf2e523873cb523df379676067bf9785a0f456)
1 /* This code placed in the public domain by Mark W. Eichin */
2 
3 #include <stdio.h>
4 #include "autoconf.h"
5 
6 #ifdef HAVE_SYS_TYPES_H
7 #include <sys/types.h>
8 #endif
9 #ifdef HAVE_SYS_TIME_H
10 #include <sys/time.h>
11 #ifdef TIME_WITH_SYS_TIME
12 #include <time.h>
13 #endif
14 #else
15 #include <time.h>
16 #endif
17 
18 /* take a struct tm, return seconds from GMT epoch */
19 /* like mktime, this ignores tm_wday and tm_yday. */
20 /* unlike mktime, this does not set them... it only passes a return value. */
21 
22 static const int days_in_month[12] = {
23 0,				/* jan 31 */
24 31,				/* feb 28 */
25 59,				/* mar 31 */
26 90,				/* apr 30 */
27 120,				/* may 31 */
28 151,				/* jun 30 */
29 181,				/* jul 31 */
30 212,				/* aug 31 */
31 243,				/* sep 30 */
32 273,				/* oct 31 */
33 304,				/* nov 30 */
34 334				/* dec 31 */
35 };
36 
37 #define hasleapday(year) (year%400?(year%100?(year%4?0:1):0):1)
38 
krb5int_gmt_mktime(struct tm * t)39 time_t krb5int_gmt_mktime(struct tm *t)
40 {
41   time_t accum;
42 
43 #define assert_time(cnd) if(!(cnd)) return (time_t) -1
44 
45   /*
46    * For 32-bit signed time_t centered on 1/1/1970, the range is:
47    * time 0x80000000 -> Fri Dec 13 16:45:52 1901
48    * time 0x7fffffff -> Mon Jan 18 22:14:07 2038
49    *
50    * So years 1901 and 2038 are allowable, but we can't encode all
51    * dates in those years, and we're not doing overflow/underflow
52    * checking for such cases.
53    */
54   assert_time(t->tm_year>=1);
55   /* assert_time(t->tm_year<=138); - see below */
56 
57   assert_time(t->tm_mon>=0);
58   assert_time(t->tm_mon<=11);
59   assert_time(t->tm_mday>=1);
60   assert_time(t->tm_mday<=31);
61   assert_time(t->tm_hour>=0);
62   assert_time(t->tm_hour<=23);
63   assert_time(t->tm_min>=0);
64   assert_time(t->tm_min<=59);
65   assert_time(t->tm_sec>=0);
66   assert_time(t->tm_sec<=62);
67 
68 #undef assert_time
69 
70   /*
71    * Some KDCs (like Windows Server 2025) now issue expiration timestamps above
72    * the maximum signed 32-bit value. For now, clamp those to 32-bit time_t.
73    * RFC4120 generally leaves it to the implementation to decide whether a
74    * stricter expiration is warranted, and all timestamps whose exact value is
75    * protocol-meaningful represent current time or time in the recent past.
76    */
77   if (t->tm_year >= 138) {
78     t->tm_year = 138;
79     t->tm_mon = 0;
80     t->tm_mday = 1;
81     t->tm_hour = 0;
82     t->tm_min = 0;
83     t->tm_sec = 0;
84   }
85 
86   accum = t->tm_year - 70;
87   accum *= 365;			/* 365 days/normal year */
88 
89   /* add in leap day for all previous years */
90   if (t->tm_year >= 70)
91     accum += (t->tm_year - 69) / 4;
92   else
93     accum -= (72 - t->tm_year) / 4;
94   /* add in leap day for this year */
95   if(t->tm_mon >= 2)		/* march or later */
96     if(hasleapday((t->tm_year + 1900))) accum += 1;
97 
98   accum += days_in_month[t->tm_mon];
99   accum += t->tm_mday-1;	/* days of month are the only 1-based field */
100   accum *= 24;			/* 24 hour/day */
101   accum += t->tm_hour;
102   accum *= 60;			/* 60 minute/hour */
103   accum += t->tm_min;
104   accum *= 60;			/* 60 seconds/minute */
105   accum += t->tm_sec;
106 
107   return accum;
108 }
109 
110 #ifdef TEST_LEAP
111 int
main(int argc,char * argv[])112 main (int argc, char *argv[])
113 {
114   int yr;
115   time_t t;
116   struct tm tm = {
117     .tm_mon = 0, .tm_mday = 1,
118     .tm_hour = 0, .tm_min = 0, .tm_sec = 0,
119   };
120   for (yr = 60; yr <= 104; yr++)
121     {
122       printf ("1/1/%d%c -> ", 1900 + yr, hasleapday((1900+yr)) ? '*' : ' ');
123       tm.tm_year = yr;
124       t = gmt_mktime (&tm);
125       if (t == (time_t) -1)
126 	printf ("-1\n");
127       else
128 	{
129 	  long u;
130 	  if (t % (24 * 60 * 60))
131 	    printf ("(not integral multiple of days) ");
132 	  u = t / (24 * 60 * 60);
133 	  printf ("%3ld*365%+ld\t0x%08lx\n",
134 		  (long) (u / 365), (long) (u % 365),
135 		  (long) t);
136 	}
137     }
138   t = 0x80000000, printf ("time 0x%lx -> %s", t, ctime (&t));
139   t = 0x7fffffff, printf ("time 0x%lx -> %s", t, ctime (&t));
140   return 0;
141 }
142 #endif
143