1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * lib/kadm/str_conv.c
10  *
11  * Copyright 1995, 1999 by the Massachusetts Institute of Technology.
12  * All Rights Reserved.
13  *
14  * Export of this software from the United States of America may
15  *   require a specific license from the United States Government.
16  *   It is the responsibility of any person or organization contemplating
17  *   export to obtain such a license before exporting.
18  *
19  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20  * distribute this software and its documentation for any purpose and
21  * without fee is hereby granted, provided that the above copyright
22  * notice appear in all copies and that both that copyright notice and
23  * this permission notice appear in supporting documentation, and that
24  * the name of M.I.T. not be used in advertising or publicity pertaining
25  * to distribution of the software without specific, written prior
26  * permission.  Furthermore if you modify this software you must label
27  * your software as modified software and not distribute it in such a
28  * fashion that it might be confused with the original M.I.T. software.
29  * M.I.T. makes no representations about the suitability of
30  * this software for any purpose.  It is provided "as is" without express
31  * or implied warranty.
32  *
33  */
34 
35 /*
36  * str_conv.c - Convert between strings and Kerberos internal data.
37  */
38 
39 /*
40  * Table of contents:
41  *
42  * String decoding:
43  * ----------------
44  * krb5_string_to_salttype()	- Convert string to salttype (krb5_int32)
45  * krb5_string_to_timestamp()	- Convert string to krb5_timestamp.
46  * krb5_string_to_deltat()	- Convert string to krb5_deltat.
47  *
48  * String encoding:
49  * ----------------
50  * krb5_salttype_to_string()	- Convert salttype (krb5_int32) to string.
51  * krb5_timestamp_to_string()	- Convert krb5_timestamp to string.
52  * krb5_timestamp_to_sfstring()	- Convert krb5_timestamp to short filled string
53  * krb5_deltat_to_string()	- Convert krb5_deltat to string.
54  */
55 
56 #include <k5-int.h>
57 
58 /* Salt type conversions */
59 
60 /*
61  * Local data structures.
62  */
63 struct salttype_lookup_entry {
64     krb5_int32		stt_enctype;		/* Salt type		*/
65     const char *	stt_specifier;		/* How to recognize it	*/
66     const char *	stt_output;		/* How to spit it out	*/
67 };
68 
69 /*
70  * Lookup tables.
71  */
72 
73 static const struct salttype_lookup_entry salttype_table[] = {
74 /* salt type			input specifier	output string  */
75 /*-----------------------------	--------------- ---------------*/
76 { KRB5_KDB_SALTTYPE_NORMAL,	"normal",	"Version 5"	  },
77 { KRB5_KDB_SALTTYPE_V4,		"v4",		"Version 4"	  },
78 { KRB5_KDB_SALTTYPE_NOREALM,	"norealm",	"Version 5 - No Realm" },
79 { KRB5_KDB_SALTTYPE_ONLYREALM,	"onlyrealm",	"Version 5 - Realm Only" },
80 { KRB5_KDB_SALTTYPE_SPECIAL,	"special",	"Special" },
81 { KRB5_KDB_SALTTYPE_AFS3,	"afs3",		"AFS version 3"    }
82 };
83 static const int salttype_table_nents = sizeof(salttype_table)/
84 					sizeof(salttype_table[0]);
85 
86 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
87 krb5_string_to_salttype(string, salttypep)
88     char	FAR * string;
89     krb5_int32	FAR * salttypep;
90 {
91     int i;
92     int found;
93 
94     found = 0;
95     for (i=0; i<salttype_table_nents; i++) {
96 	if (!strcasecmp(string, salttype_table[i].stt_specifier)) {
97 	    found = 1;
98 	    *salttypep = salttype_table[i].stt_enctype;
99 	    break;
100 	}
101     }
102     return((found) ? 0 : EINVAL);
103 }
104 
105 /*
106  * Internal datatype to string routines.
107  *
108  * These routines return 0 for success, EINVAL for invalid parameter, ENOMEM
109  * if the supplied buffer/length will not contain the output.
110  */
111 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
112 krb5_salttype_to_string(salttype, buffer, buflen)
113     krb5_int32	salttype;
114     char	FAR * buffer;
115     size_t	buflen;
116 {
117     int i;
118     const char *out;
119 
120     out = (char *) NULL;
121     for (i=0; i<salttype_table_nents; i++) {
122 	if (salttype ==  salttype_table[i].stt_enctype) {
123 	    out = salttype_table[i].stt_output;
124 	    break;
125 	}
126     }
127     if (out) {
128 	if (buflen > strlen(out))
129 	    strcpy(buffer, out);
130 	else
131 	    out = (char *) NULL;
132 	return((out) ? 0 : ENOMEM);
133     }
134     else
135 	return(EINVAL);
136 }
137 
138 /* (absolute) time conversions */
139 
140 #ifndef HAVE_STRFTIME
141 #undef strftime
142 #define strftime my_strftime
143 static size_t strftime (char *, size_t, const char *, const struct tm *);
144 #endif
145 
146 #ifndef HAVE_STRPTIME
147 #undef strptime
148 #define strptime my_strptime
149 static char *strptime (const char *, const char *, struct tm *);
150 #endif
151 
152 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
153 krb5_string_to_timestamp(string, timestampp)
154     char		FAR * string;
155     krb5_timestamp	FAR * timestampp;
156 {
157     int i,found;
158     struct tm timebuf, nowbuf;
159     time_t now;
160     char *s;
161     static const char * const atime_format_table[] = {
162 	"%Y" "%m%d%H" "%M" "%S",/* yyyymmddhhmmss		*/
163 	"%Y.%m.%d.%H.%M.%S",	/* yyyy.mm.dd.hh.mm.ss		*/
164 	"%y%m%d%H" "%M" "%S",	/* yymmddhhmmss			*/
165 	"%y.%m.%d.%H.%M.%S",	/* yy.mm.dd.hh.mm.ss		*/
166 	"%y%m%d%H" "%M",	/* yymmddhhmm			*/
167 	"%H" "%M" "%S",		/* hhmmss			*/
168 	"%H" "%M",		/* hhmm				*/
169 	"%T",			/* hh:mm:ss			*/
170 	"%R",			/* hh:mm			*/
171 	/* The following not really supported unless native strptime present */
172 	"%x:%X",		/* locale-dependent short format */
173 	"%d-%b-%Y:%T",		/* dd-month-yyyy:hh:mm:ss	*/
174 	"%d-%b-%Y:%R"		/* dd-month-yyyy:hh:mm		*/
175     };
176     static const int atime_format_table_nents =
177 	sizeof(atime_format_table)/sizeof(atime_format_table[0]);
178 
179     found = 0;
180     for (i=0; i<atime_format_table_nents; i++) {
181 	s = strptime(string, atime_format_table[i], &timebuf);
182 	/* make sure the entire string was parsed */
183 	if (s && (*s == '\0')) {
184 		/* If only time and no date was provided, assume today */
185 		if ((timebuf.tm_mday == 0) && (timebuf.tm_mon == 0) &&
186 			(timebuf.tm_year == 0)) {
187 			now = time((time_t *) NULL);
188 			(void) memcpy(&nowbuf, localtime(&now), sizeof(timebuf));
189 			timebuf.tm_mday = nowbuf.tm_mday;
190 			timebuf.tm_mon = nowbuf.tm_mon;
191 			timebuf.tm_year = nowbuf.tm_year;
192 		}
193 		found = 1;
194 		break;
195 	}
196     }
197     if (found) {
198 	if ((*timestampp = (krb5_timestamp) mktime(&timebuf)) != -1) {
199 		if (timebuf.tm_isdst == 1) {
200 			*timestampp -= (timezone - altzone);
201 		}
202 		return (0);
203 	}
204     }
205     return(EINVAL);
206 }
207 
208 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
209 krb5_timestamp_to_string(timestamp, buffer, buflen)
210     krb5_timestamp	timestamp;
211     char		FAR * buffer;
212     size_t		buflen;
213 {
214     int ret;
215     time_t timestamp2 = timestamp;
216 
217     ret = strftime(buffer, buflen, "%c", localtime(&timestamp2));
218     if (ret == 0 || ret == buflen)
219 	return(ENOMEM);
220     return(0);
221 }
222 
223 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
224 krb5_timestamp_to_sfstring(timestamp, buffer, buflen, pad)
225     krb5_timestamp	timestamp;
226     char		FAR * buffer;
227     size_t		buflen;
228     char		FAR * pad;
229 {
230     struct tm	*tmp;
231     size_t i;
232     size_t	ndone;
233     time_t timestamp2 = timestamp;
234 
235     static const char * const sftime_format_table[] = {
236 	"%c",			/* Default locale-dependent date and time */
237 	"%d %b %Y %T",		/* dd mon yyyy hh:mm:ss			*/
238 	"%x %X",		/* locale-dependent short format	*/
239 	"%d/%m/%Y %R"		/* dd/mm/yyyy hh:mm			*/
240     };
241     static const int sftime_format_table_nents =
242 	sizeof(sftime_format_table)/sizeof(sftime_format_table[0]);
243 
244     tmp = localtime(&timestamp2);
245     ndone = 0;
246     for (i=0; i<sftime_format_table_nents; i++) {
247 	if ((ndone = strftime(buffer, buflen, sftime_format_table[i], tmp)))
248 	    break;
249     }
250     if (!ndone) {
251 #define sftime_default_len	2+1+2+1+4+1+2+1+2+1
252 	if (buflen >= sftime_default_len) {
253 	    sprintf(buffer, "%02d/%02d/%4d %02d:%02d",
254 		    tmp->tm_mday, tmp->tm_mon+1, 1900+tmp->tm_year,
255 		    tmp->tm_hour, tmp->tm_min);
256 	    ndone = strlen(buffer);
257 	}
258     }
259     if (ndone && pad) {
260 	for (i=ndone; i<buflen-1; i++)
261 	    buffer[i] = *pad;
262 	buffer[buflen-1] = '\0';
263     }
264     return((ndone) ? 0 : ENOMEM);
265 }
266 
267 #ifdef SUNW_INC_DEAD_CODE
268 /* relative time (delta-t) conversions */
269 
270 /* string->deltat is in deltat.y */
271 
272 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
273 krb5_deltat_to_string(deltat, buffer, buflen)
274     krb5_deltat	deltat;
275     char	FAR * buffer;
276     size_t	buflen;
277 {
278     int			days, hours, minutes, seconds;
279     krb5_deltat		dt;
280 
281     /*
282      * We want something like ceil(log10(2**(nbits-1))) + 1.  That log
283      * value is log10(2)*(nbits-1) or log10(2**8)*(nbits-1)/8.  So,
284      * 2.4... is log10(256), rounded up.  Add one to handle leading
285      * minus, and one more to force int cast to round the value up.
286      * This doesn't include room for a trailing nul.
287      *
288      * This will break if bytes are more than 8 bits.
289      */
290 #define MAX_CHARS_FOR_INT_TYPE(TYPE)	((int) (2 + 2.408241 * sizeof (TYPE)))
291     char tmpbuf[MAX_CHARS_FOR_INT_TYPE(int) * 4 + 8];
292 
293     days = (int) (deltat / (24*3600L));
294     dt = deltat % (24*3600L);
295     hours = (int) (dt / 3600);
296     dt %= 3600;
297     minutes = (int) (dt / 60);
298     seconds = (int) (dt % 60);
299 
300     memset (tmpbuf, 0, sizeof (tmpbuf));
301     if (days == 0)
302 	sprintf(buffer, "%d:%02d:%02d", hours, minutes, seconds);
303     else if (hours || minutes || seconds)
304 	sprintf(buffer, "%d %s %02d:%02d:%02d", days,
305 		(days > 1) ? "days" : "day",
306 		hours, minutes, seconds);
307     else
308 	sprintf(buffer, "%d %s", days,
309 		(days > 1) ? "days" : "day");
310     if (tmpbuf[sizeof(tmpbuf)-1] != 0)
311 	/* Something must be very wrong with my math above, or the
312 	   assumptions going into it...  */
313 	abort ();
314     if (strlen (tmpbuf) > buflen)
315 	return ENOMEM;
316     else
317 	strncpy (buffer, tmpbuf, buflen);
318     return 0;
319 }
320 #endif /* SUNW_INC_DEAD_CODE */
321 
322 #undef __P
323 #define __P(X) X
324 
325 #if !defined (HAVE_STRFTIME) || !defined (HAVE_STRPTIME)
326 #undef _CurrentTimeLocale
327 #define _CurrentTimeLocale (&dummy_locale_info)
328 
329 struct dummy_locale_info_t {
330     char d_t_fmt[15];
331     char t_fmt_ampm[12];
332     char t_fmt[9];
333     char d_fmt[9];
334     char day[7][10];
335     char abday[7][4];
336     char mon[12][10];
337     char abmon[12][4];
338     char am_pm[2][3];
339 };
340 static const struct dummy_locale_info_t dummy_locale_info = {
341     "%a %b %d %X %Y",		/* %c */
342     "%I:%M:%S %p",		/* %r */
343     "%H:%M:%S",			/* %X */
344     "%m/%d/%y",			/* %x */
345     { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
346       "Saturday" },
347     { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
348     { "January", "February", "March", "April", "May", "June",
349       "July", "August", "September", "October", "November", "December" },
350     { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
351       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" },
352     { "AM", "PM" },
353 };
354 #undef  TM_YEAR_BASE
355 #define TM_YEAR_BASE 1900
356 #endif
357 
358 #ifndef HAVE_STRFTIME
359 #undef  DAYSPERLYEAR
360 #define DAYSPERLYEAR 366
361 #undef  DAYSPERNYEAR
362 #define DAYSPERNYEAR 365
363 #undef  DAYSPERWEEK
364 #define DAYSPERWEEK 7
365 #undef  isleap
366 #define isleap(N)	((N % 4) == 0 && (N % 100 != 0 || N % 400 == 0))
367 #undef  tzname
368 #define tzname my_tzname
369 static const char *const tzname[2] = { 0, 0 };
370 #undef  tzset
371 #define tzset()
372 
373 #include "strftime.c"
374 #endif
375 
376 #ifndef HAVE_STRPTIME
377 #include "strptime.c"
378 #endif
379