1/*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "kadmin_locl.h"
35#include <parse_units.h>
36
37/*
38 * util.c - functions for parsing, unparsing, and editing different
39 * types of data used in kadmin.
40 */
41
42static int
43get_response(const char *prompt, const char *def, char *buf, size_t len);
44
45/*
46 * attributes
47 */
48
49struct units kdb_attrs[] = {
50    { "allow-digest",		KRB5_KDB_ALLOW_DIGEST },
51    { "allow-kerberos4",	KRB5_KDB_ALLOW_KERBEROS4 },
52    { "trusted-for-delegation",	KRB5_KDB_TRUSTED_FOR_DELEGATION },
53    { "ok-as-delegate",		KRB5_KDB_OK_AS_DELEGATE },
54    { "new-princ",		KRB5_KDB_NEW_PRINC },
55    { "support-desmd5",		KRB5_KDB_SUPPORT_DESMD5 },
56    { "pwchange-service",	KRB5_KDB_PWCHANGE_SERVICE },
57    { "disallow-svr",		KRB5_KDB_DISALLOW_SVR },
58    { "requires-pw-change",	KRB5_KDB_REQUIRES_PWCHANGE },
59    { "requires-hw-auth",	KRB5_KDB_REQUIRES_HW_AUTH },
60    { "requires-pre-auth",	KRB5_KDB_REQUIRES_PRE_AUTH },
61    { "disallow-all-tix",	KRB5_KDB_DISALLOW_ALL_TIX },
62    { "disallow-dup-skey",	KRB5_KDB_DISALLOW_DUP_SKEY },
63    { "disallow-proxiable",	KRB5_KDB_DISALLOW_PROXIABLE },
64    { "disallow-renewable",	KRB5_KDB_DISALLOW_RENEWABLE },
65    { "disallow-tgt-based",	KRB5_KDB_DISALLOW_TGT_BASED },
66    { "disallow-forwardable",	KRB5_KDB_DISALLOW_FORWARDABLE },
67    { "disallow-postdated",	KRB5_KDB_DISALLOW_POSTDATED },
68    { NULL, 0 }
69};
70
71/*
72 * convert the attributes in `attributes' into a printable string
73 * in `str, len'
74 */
75
76void
77attributes2str(krb5_flags attributes, char *str, size_t len)
78{
79    unparse_flags (attributes, kdb_attrs, str, len);
80}
81
82/*
83 * convert the string in `str' into attributes in `flags'
84 * return 0 if parsed ok, else -1.
85 */
86
87int
88str2attributes(const char *str, krb5_flags *flags)
89{
90    int res;
91
92    res = parse_flags (str, kdb_attrs, *flags);
93    if (res < 0)
94	return res;
95    else {
96	*flags = res;
97	return 0;
98    }
99}
100
101/*
102 * try to parse the string `resp' into attributes in `attr', also
103 * setting the `bit' in `mask' if attributes are given and valid.
104 */
105
106int
107parse_attributes (const char *resp, krb5_flags *attr, int *mask, int bit)
108{
109    krb5_flags tmp = *attr;
110
111    if (str2attributes(resp, &tmp) == 0) {
112	*attr = tmp;
113	if (mask)
114	    *mask |= bit;
115	return 0;
116    } else if(*resp == '?') {
117	print_flags_table (kdb_attrs, stderr);
118    } else {
119	fprintf (stderr, "Unable to parse \"%s\"\n", resp);
120    }
121    return -1;
122}
123
124/*
125 * allow the user to edit the attributes in `attr', prompting with `prompt'
126 */
127
128int
129edit_attributes (const char *prompt, krb5_flags *attr, int *mask, int bit)
130{
131    char buf[1024], resp[1024];
132
133    if (mask && (*mask & bit))
134	return 0;
135
136    attributes2str(*attr, buf, sizeof(buf));
137    for (;;) {
138	if(get_response("Attributes", buf, resp, sizeof(resp)) != 0)
139	    return 1;
140	if (resp[0] == '\0')
141	    break;
142	if (parse_attributes (resp, attr, mask, bit) == 0)
143	    break;
144    }
145    return 0;
146}
147
148/*
149 * time_t
150 * the special value 0 means ``never''
151 */
152
153/*
154 * Convert the time `t' to a string representation in `str' (of max
155 * size `len').  If include_time also include time, otherwise just
156 * date.
157 */
158
159void
160time_t2str(time_t t, char *str, size_t len, int include_time)
161{
162    if(t) {
163	if(include_time)
164	    strftime(str, len, "%Y-%m-%d %H:%M:%S UTC", gmtime(&t));
165	else
166	    strftime(str, len, "%Y-%m-%d", gmtime(&t));
167    } else
168	snprintf(str, len, "never");
169}
170
171/*
172 * Convert the time representation in `str' to a time in `time'.
173 * Return 0 if succesful, else -1.
174 */
175
176int
177str2time_t (const char *str, time_t *t)
178{
179    const char *p;
180    struct tm tm, tm2;
181
182    memset (&tm, 0, sizeof (tm));
183    memset (&tm2, 0, sizeof (tm2));
184
185    while(isspace((unsigned char)*str))
186	str++;
187
188    if (str[0] == '+') {
189	str++;
190	*t = parse_time(str, "month");
191	if (*t < 0)
192	    return -1;
193	*t += time(NULL);
194	return 0;
195    }
196
197    if(strcasecmp(str, "never") == 0) {
198	*t = 0;
199	return 0;
200    }
201
202    if(strcasecmp(str, "now") == 0) {
203	*t = time(NULL);
204	return 0;
205    }
206
207    p = strptime (str, "%Y-%m-%d", &tm);
208
209    if (p == NULL)
210	return -1;
211
212    while(isspace((unsigned char)*p))
213	p++;
214
215    /* XXX this is really a bit optimistic, we should really complain
216       if there was a problem parsing the time */
217    if(p[0] != '\0' && strptime (p, "%H:%M:%S", &tm2) != NULL) {
218	tm.tm_hour = tm2.tm_hour;
219	tm.tm_min  = tm2.tm_min;
220	tm.tm_sec  = tm2.tm_sec;
221    } else {
222	/* Do it on the end of the day */
223	tm.tm_hour = 23;
224	tm.tm_min  = 59;
225	tm.tm_sec  = 59;
226    }
227
228    *t = tm2time (tm, 0);
229    return 0;
230}
231
232/*
233 * try to parse the time in `resp' storing it in `value'
234 */
235
236int
237parse_timet (const char *resp, krb5_timestamp *value, int *mask, int bit)
238{
239    time_t tmp;
240
241    if (str2time_t(resp, &tmp) == 0) {
242	*value = tmp;
243	if(mask)
244	    *mask |= bit;
245	return 0;
246    }
247    if(*resp != '?')
248	fprintf (stderr, "Unable to parse time \"%s\"\n", resp);
249    fprintf (stderr, "Print date on format YYYY-mm-dd [hh:mm:ss]\n");
250    return -1;
251}
252
253/*
254 * allow the user to edit the time in `value'
255 */
256
257int
258edit_timet (const char *prompt, krb5_timestamp *value, int *mask, int bit)
259{
260    char buf[1024], resp[1024];
261
262    if (mask && (*mask & bit))
263	return 0;
264
265    time_t2str (*value, buf, sizeof (buf), 0);
266
267    for (;;) {
268	if(get_response(prompt, buf, resp, sizeof(resp)) != 0)
269	    return 1;
270	if (parse_timet (resp, value, mask, bit) == 0)
271	    break;
272    }
273    return 0;
274}
275
276/*
277 * deltat
278 * the special value 0 means ``unlimited''
279 */
280
281/*
282 * convert the delta_t value in `t' into a printable form in `str, len'
283 */
284
285void
286deltat2str(unsigned t, char *str, size_t len)
287{
288    if(t == 0 || t == INT_MAX)
289	snprintf(str, len, "unlimited");
290    else
291	unparse_time(t, str, len);
292}
293
294/*
295 * parse the delta value in `str', storing result in `*delta'
296 * return 0 if ok, else -1
297 */
298
299int
300str2deltat(const char *str, krb5_deltat *delta)
301{
302    int res;
303
304    if(strcasecmp(str, "unlimited") == 0) {
305	*delta = 0;
306	return 0;
307    }
308    res = parse_time(str, "day");
309    if (res < 0)
310	return res;
311    else {
312	*delta = res;
313	return 0;
314    }
315}
316
317/*
318 * try to parse the string in `resp' into a deltad in `value'
319 * `mask' will get the bit `bit' set if a value was given.
320 */
321
322int
323parse_deltat (const char *resp, krb5_deltat *value, int *mask, int bit)
324{
325    krb5_deltat tmp;
326
327    if (str2deltat(resp, &tmp) == 0) {
328	*value = tmp;
329	if (mask)
330	    *mask |= bit;
331	return 0;
332    } else if(*resp == '?') {
333	print_time_table (stderr);
334    } else {
335	fprintf (stderr, "Unable to parse time \"%s\"\n", resp);
336    }
337    return -1;
338}
339
340/*
341 * allow the user to edit the deltat in `value'
342 */
343
344int
345edit_deltat (const char *prompt, krb5_deltat *value, int *mask, int bit)
346{
347    char buf[1024], resp[1024];
348
349    if (mask && (*mask & bit))
350	return 0;
351
352    deltat2str(*value, buf, sizeof(buf));
353    for (;;) {
354	if(get_response(prompt, buf, resp, sizeof(resp)) != 0)
355	    return 1;
356	if (parse_deltat (resp, value, mask, bit) == 0)
357	    break;
358    }
359    return 0;
360}
361
362/*
363 * allow the user to edit `ent'
364 */
365
366void
367set_defaults(kadm5_principal_ent_t ent, int *mask,
368	     kadm5_principal_ent_t default_ent, int default_mask)
369{
370    if (default_ent
371	&& (default_mask & KADM5_MAX_LIFE)
372	&& !(*mask & KADM5_MAX_LIFE))
373	ent->max_life = default_ent->max_life;
374
375    if (default_ent
376	&& (default_mask & KADM5_MAX_RLIFE)
377	&& !(*mask & KADM5_MAX_RLIFE))
378	ent->max_renewable_life = default_ent->max_renewable_life;
379
380    if (default_ent
381	&& (default_mask & KADM5_PRINC_EXPIRE_TIME)
382	&& !(*mask & KADM5_PRINC_EXPIRE_TIME))
383	ent->princ_expire_time = default_ent->princ_expire_time;
384
385    if (default_ent
386	&& (default_mask & KADM5_PW_EXPIRATION)
387	&& !(*mask & KADM5_PW_EXPIRATION))
388	ent->pw_expiration = default_ent->pw_expiration;
389
390    if (default_ent
391	&& (default_mask & KADM5_ATTRIBUTES)
392	&& !(*mask & KADM5_ATTRIBUTES))
393	ent->attributes = default_ent->attributes & ~KRB5_KDB_DISALLOW_ALL_TIX;
394}
395
396int
397edit_entry(kadm5_principal_ent_t ent, int *mask,
398	   kadm5_principal_ent_t default_ent, int default_mask)
399{
400
401    set_defaults(ent, mask, default_ent, default_mask);
402
403    if(edit_deltat ("Max ticket life", &ent->max_life, mask,
404		    KADM5_MAX_LIFE) != 0)
405	return 1;
406
407    if(edit_deltat ("Max renewable life", &ent->max_renewable_life, mask,
408		    KADM5_MAX_RLIFE) != 0)
409	return 1;
410
411    if(edit_timet ("Principal expiration time", &ent->princ_expire_time, mask,
412		   KADM5_PRINC_EXPIRE_TIME) != 0)
413	return 1;
414
415    if(edit_timet ("Password expiration time", &ent->pw_expiration, mask,
416		   KADM5_PW_EXPIRATION) != 0)
417	return 1;
418
419    if(edit_attributes ("Attributes", &ent->attributes, mask,
420			KADM5_ATTRIBUTES) != 0)
421	return 1;
422
423    return 0;
424}
425
426/*
427 * Parse the arguments, set the fields in `ent' and the `mask' for the
428 * entries having been set.
429 * Return 1 on failure and 0 on success.
430 */
431
432int
433set_entry(krb5_context contextp,
434	  kadm5_principal_ent_t ent,
435	  int *mask,
436	  const char *max_ticket_life,
437	  const char *max_renewable_life,
438	  const char *expiration,
439	  const char *pw_expiration,
440	  const char *attributes)
441{
442    if (max_ticket_life != NULL) {
443	if (parse_deltat (max_ticket_life, &ent->max_life,
444			  mask, KADM5_MAX_LIFE)) {
445	    krb5_warnx (contextp, "unable to parse `%s'", max_ticket_life);
446	    return 1;
447	}
448    }
449    if (max_renewable_life != NULL) {
450	if (parse_deltat (max_renewable_life, &ent->max_renewable_life,
451			  mask, KADM5_MAX_RLIFE)) {
452	    krb5_warnx (contextp, "unable to parse `%s'", max_renewable_life);
453	    return 1;
454	}
455    }
456
457    if (expiration) {
458	if (parse_timet (expiration, &ent->princ_expire_time,
459			mask, KADM5_PRINC_EXPIRE_TIME)) {
460	    krb5_warnx (contextp, "unable to parse `%s'", expiration);
461	    return 1;
462	}
463    }
464    if (pw_expiration) {
465	if (parse_timet (pw_expiration, &ent->pw_expiration,
466			 mask, KADM5_PW_EXPIRATION)) {
467	    krb5_warnx (contextp, "unable to parse `%s'", pw_expiration);
468	    return 1;
469	}
470    }
471    if (attributes != NULL) {
472	if (parse_attributes (attributes, &ent->attributes,
473			      mask, KADM5_ATTRIBUTES)) {
474	    krb5_warnx (contextp, "unable to parse `%s'", attributes);
475	    return 1;
476	}
477    }
478    return 0;
479}
480
481/*
482 * Does `string' contain any globing characters?
483 */
484
485static int
486is_expression(const char *string)
487{
488    const char *p;
489    int quote = 0;
490
491    for(p = string; *p; p++) {
492	if(quote) {
493	    quote = 0;
494	    continue;
495	}
496	if(*p == '\\')
497	    quote++;
498	else if(strchr("[]*?", *p) != NULL)
499	    return 1;
500    }
501    return 0;
502}
503
504/*
505 * Loop over all principals matching exp.  If any of calls to `func'
506 * failes, the first error is returned when all principals are
507 * processed.
508 */
509int
510foreach_principal(const char *exp_str,
511		  int (*func)(krb5_principal, void*),
512		  const char *funcname,
513		  void *data)
514{
515    char **princs = NULL;
516    int num_princs = 0;
517    int i;
518    krb5_error_code saved_ret = 0, ret = 0;
519    krb5_principal princ_ent;
520    int is_expr;
521
522    /* if this isn't an expression, there is no point in wading
523       through the whole database looking for matches */
524    is_expr = is_expression(exp_str);
525    if(is_expr)
526	ret = kadm5_get_principals(kadm_handle, exp_str, &princs, &num_princs);
527    if(!is_expr || ret == KADM5_AUTH_LIST) {
528	/* we might be able to perform the requested opreration even
529           if we're not allowed to list principals */
530	num_princs = 1;
531	princs = malloc(sizeof(*princs));
532	if(princs == NULL)
533	    return ENOMEM;
534	princs[0] = strdup(exp_str);
535	if(princs[0] == NULL){
536	    free(princs);
537	    return ENOMEM;
538	}
539    } else if(ret) {
540	krb5_warn(context, ret, "kadm5_get_principals");
541	return ret;
542    }
543    for(i = 0; i < num_princs; i++) {
544	ret = krb5_parse_name(context, princs[i], &princ_ent);
545	if(ret){
546	    krb5_warn(context, ret, "krb5_parse_name(%s)", princs[i]);
547	    continue;
548	}
549	ret = (*func)(princ_ent, data);
550	if(ret) {
551	    krb5_clear_error_message(context);
552	    krb5_warn(context, ret, "%s %s", funcname, princs[i]);
553	    if (saved_ret == 0)
554		saved_ret = ret;
555	}
556	krb5_free_principal(context, princ_ent);
557    }
558    if (ret == 0 && saved_ret != 0)
559	ret = saved_ret;
560    kadm5_free_name_list(kadm_handle, princs, &num_princs);
561    return ret;
562}
563
564/*
565 * prompt with `prompt' and default value `def', and store the reply
566 * in `buf, len'
567 */
568
569#include <setjmp.h>
570
571static jmp_buf jmpbuf;
572
573static void
574interrupt(int sig)
575{
576    longjmp(jmpbuf, 1);
577}
578
579static int
580get_response(const char *prompt, const char *def, char *buf, size_t len)
581{
582    char *p;
583    void (*osig)(int);
584
585    osig = signal(SIGINT, interrupt);
586    if(setjmp(jmpbuf)) {
587	signal(SIGINT, osig);
588	fprintf(stderr, "\n");
589	return 1;
590    }
591
592    fprintf(stderr, "%s [%s]:", prompt, def);
593    if(fgets(buf, len, stdin) == NULL) {
594	int save_errno = errno;
595	if(ferror(stdin))
596	    krb5_err(context, 1, save_errno, "<stdin>");
597	signal(SIGINT, osig);
598	return 1;
599    }
600    p = strchr(buf, '\n');
601    if(p)
602	*p = '\0';
603    if(strcmp(buf, "") == 0)
604	strlcpy(buf, def, len);
605    signal(SIGINT, osig);
606    return 0;
607}
608
609/*
610 * return [0, 16) or -1
611 */
612
613static int
614hex2n (char c)
615{
616    static char hexdigits[] = "0123456789abcdef";
617    const char *p;
618
619    p = strchr (hexdigits, tolower((unsigned char)c));
620    if (p == NULL)
621	return -1;
622    else
623	return p - hexdigits;
624}
625
626/*
627 * convert a key in a readable format into a keyblock.
628 * return 0 iff succesful, otherwise `err' should point to an error message
629 */
630
631int
632parse_des_key (const char *key_string, krb5_key_data *key_data,
633	       const char **error)
634{
635    const char *p = key_string;
636    unsigned char bits[8];
637    int i;
638
639    if (strlen (key_string) != 16) {
640	*error = "bad length, should be 16 for DES key";
641	return 1;
642    }
643    for (i = 0; i < 8; ++i) {
644	int d1, d2;
645
646	d1 = hex2n(p[2 * i]);
647	d2 = hex2n(p[2 * i + 1]);
648	if (d1 < 0 || d2 < 0) {
649	    *error = "non-hex character";
650	    return 1;
651	}
652	bits[i] = (d1 << 4) | d2;
653    }
654    for (i = 0; i < 3; ++i) {
655	key_data[i].key_data_ver  = 2;
656	key_data[i].key_data_kvno = 0;
657	/* key */
658	key_data[i].key_data_type[0]     = ETYPE_DES_CBC_CRC;
659	key_data[i].key_data_length[0]   = 8;
660	key_data[i].key_data_contents[0] = malloc(8);
661	if (key_data[i].key_data_contents[0] == NULL) {
662	    *error = "malloc";
663	    return ENOMEM;
664	}
665	memcpy (key_data[i].key_data_contents[0], bits, 8);
666	/* salt */
667	key_data[i].key_data_type[1]     = KRB5_PW_SALT;
668	key_data[i].key_data_length[1]   = 0;
669	key_data[i].key_data_contents[1] = NULL;
670    }
671    key_data[0].key_data_type[0] = ETYPE_DES_CBC_MD5;
672    key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
673    return 0;
674}
675