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