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 * try to parse the string `resp' into policy in `attr', also
150 * setting the `bit' in `mask' if attributes are given and valid.
151 */
152
153#define VALID_POLICY_NAME_CHARS \
154	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
155
156int
157parse_policy (const char *resp, char **policy, int *mask, int bit)
158{
159    if (strcasecmp(resp, "none") == 0) {
160	return 0;
161    } else if (strspn(resp, VALID_POLICY_NAME_CHARS) == strlen(resp) &&
162	*resp != '\0') {
163
164	*policy = strdup(resp);
165	if (*policy == NULL) {
166	    fprintf (stderr, "Out of memory");
167	    return -1;
168	}
169	if (mask)
170	    *mask |= bit;
171	return 0;
172    } else if(*resp == '?') {
173	fprintf (stderr, "Policy is free string or \"none\" for no policy, "
174		 "by default the \"default\" policy is used\n");
175    } else {
176	fprintf (stderr, "Unable to parse \"%s\"\n", resp);
177    }
178    return -1;
179}
180
181/*
182 * allow the user to edit the attributes in `attr', prompting with `prompt'
183 */
184
185int
186edit_policy (const char *prompt, char **policy, int *mask, int bit)
187{
188    char buf[1024], resp[1024];
189
190    if (mask && (*mask & bit))
191	return 0;
192
193    buf[0] = '\0';
194    strlcpy(buf, "default", sizeof (buf));
195    for (;;) {
196	if(get_response("Policy", buf, resp, sizeof(resp)) != 0)
197	    return 1;
198	if (resp[0] == '\0')
199	    break;
200	if (parse_policy (resp, policy, mask, bit) == 0)
201	    break;
202    }
203    return 0;
204}
205
206/*
207 * time_t
208 * the special value 0 means ``never''
209 */
210
211/*
212 * Convert the time `t' to a string representation in `str' (of max
213 * size `len').  If include_time also include time, otherwise just
214 * date.
215 */
216
217void
218time_t2str(time_t t, char *str, size_t len, int include_time)
219{
220    if(t) {
221	if(include_time)
222	    strftime(str, len, "%Y-%m-%d %H:%M:%S UTC", gmtime(&t));
223	else
224	    strftime(str, len, "%Y-%m-%d", gmtime(&t));
225    } else
226	snprintf(str, len, "never");
227}
228
229/*
230 * Convert the time representation in `str' to a time in `time'.
231 * Return 0 if succesful, else -1.
232 */
233
234int
235str2time_t (const char *str, time_t *t)
236{
237    const char *p;
238    struct tm tm, tm2;
239
240    memset (&tm, 0, sizeof (tm));
241    memset (&tm2, 0, sizeof (tm2));
242
243    while(isspace((unsigned char)*str))
244	str++;
245
246    if (str[0] == '+') {
247	str++;
248	*t = parse_time(str, "month");
249	if (*t < 0)
250	    return -1;
251	*t += time(NULL);
252	return 0;
253    }
254
255    if(strcasecmp(str, "never") == 0) {
256	*t = 0;
257	return 0;
258    }
259
260    if(strcasecmp(str, "now") == 0) {
261	*t = time(NULL);
262	return 0;
263    }
264
265    p = strptime (str, "%Y-%m-%d", &tm);
266
267    if (p == NULL)
268	return -1;
269
270    while(isspace((unsigned char)*p))
271	p++;
272
273    /* XXX this is really a bit optimistic, we should really complain
274       if there was a problem parsing the time */
275    if(p[0] != '\0' && strptime (p, "%H:%M:%S", &tm2) != NULL) {
276	tm.tm_hour = tm2.tm_hour;
277	tm.tm_min  = tm2.tm_min;
278	tm.tm_sec  = tm2.tm_sec;
279    } else {
280	/* Do it on the end of the day */
281	tm.tm_hour = 23;
282	tm.tm_min  = 59;
283	tm.tm_sec  = 59;
284    }
285
286    *t = tm2time (tm, 0);
287    return 0;
288}
289
290/*
291 * try to parse the time in `resp' storing it in `value'
292 */
293
294int
295parse_timet (const char *resp, krb5_timestamp *value, int *mask, int bit)
296{
297    time_t tmp;
298
299    if (str2time_t(resp, &tmp) == 0) {
300	*value = tmp;
301	if(mask)
302	    *mask |= bit;
303	return 0;
304    }
305    if(*resp != '?')
306	fprintf (stderr, "Unable to parse time \"%s\"\n", resp);
307    fprintf (stderr, "Print date on format YYYY-mm-dd [hh:mm:ss]\n");
308    return -1;
309}
310
311/*
312 * allow the user to edit the time in `value'
313 */
314
315int
316edit_timet (const char *prompt, krb5_timestamp *value, int *mask, int bit)
317{
318    char buf[1024], resp[1024];
319
320    if (mask && (*mask & bit))
321	return 0;
322
323    time_t2str (*value, buf, sizeof (buf), 0);
324
325    for (;;) {
326	if(get_response(prompt, buf, resp, sizeof(resp)) != 0)
327	    return 1;
328	if (parse_timet (resp, value, mask, bit) == 0)
329	    break;
330    }
331    return 0;
332}
333
334/*
335 * deltat
336 * the special value 0 means ``unlimited''
337 */
338
339/*
340 * convert the delta_t value in `t' into a printable form in `str, len'
341 */
342
343void
344deltat2str(krb5_deltat t, char *str, size_t len)
345{
346    if(t == 0 || t == INT_MAX)
347	snprintf(str, len, "unlimited");
348    else
349	unparse_time((unsigned)t, str, len);
350}
351
352/*
353 * parse the delta value in `str', storing result in `*delta'
354 * return 0 if ok, else -1
355 */
356
357int
358str2deltat(const char *str, krb5_deltat *delta)
359{
360    int res;
361
362    if(strcasecmp(str, "unlimited") == 0) {
363	*delta = 0;
364	return 0;
365    }
366    res = parse_time(str, "day");
367    if (res < 0)
368	return res;
369    else {
370	*delta = res;
371	return 0;
372    }
373}
374
375/*
376 * try to parse the string in `resp' into a deltad in `value'
377 * `mask' will get the bit `bit' set if a value was given.
378 */
379
380int
381parse_deltat (const char *resp, krb5_deltat *value, int *mask, int bit)
382{
383    krb5_deltat tmp;
384
385    if (str2deltat(resp, &tmp) == 0) {
386	*value = tmp;
387	if (mask)
388	    *mask |= bit;
389	return 0;
390    } else if(*resp == '?') {
391	print_time_table (stderr);
392    } else {
393	fprintf (stderr, "Unable to parse time \"%s\"\n", resp);
394    }
395    return -1;
396}
397
398/*
399 * allow the user to edit the deltat in `value'
400 */
401
402int
403edit_deltat (const char *prompt, krb5_deltat *value, int *mask, int bit)
404{
405    char buf[1024], resp[1024];
406
407    if (mask && (*mask & bit))
408	return 0;
409
410    deltat2str(*value, buf, sizeof(buf));
411    for (;;) {
412	if(get_response(prompt, buf, resp, sizeof(resp)) != 0)
413	    return 1;
414	if (parse_deltat (resp, value, mask, bit) == 0)
415	    break;
416    }
417    return 0;
418}
419
420/*
421 * allow the user to edit `ent'
422 */
423
424void
425set_defaults(kadm5_principal_ent_t ent, int *mask,
426	     kadm5_principal_ent_t default_ent, int default_mask)
427{
428    if (default_ent
429	&& (default_mask & KADM5_MAX_LIFE)
430	&& !(*mask & KADM5_MAX_LIFE))
431	ent->max_life = default_ent->max_life;
432
433    if (default_ent
434	&& (default_mask & KADM5_MAX_RLIFE)
435	&& !(*mask & KADM5_MAX_RLIFE))
436	ent->max_renewable_life = default_ent->max_renewable_life;
437
438    if (default_ent
439	&& (default_mask & KADM5_PRINC_EXPIRE_TIME)
440	&& !(*mask & KADM5_PRINC_EXPIRE_TIME))
441	ent->princ_expire_time = default_ent->princ_expire_time;
442
443    if (default_ent
444	&& (default_mask & KADM5_PW_EXPIRATION)
445	&& !(*mask & KADM5_PW_EXPIRATION))
446	ent->pw_expiration = default_ent->pw_expiration;
447
448    if (default_ent
449	&& (default_mask & KADM5_ATTRIBUTES)
450	&& !(*mask & KADM5_ATTRIBUTES))
451	ent->attributes = default_ent->attributes & ~KRB5_KDB_DISALLOW_ALL_TIX;
452
453    if (default_ent
454	&& (default_mask & KADM5_POLICY)
455	&& !(*mask & KADM5_POLICY)) {
456	ent->policy = strdup(default_ent->policy);
457	if (ent->policy == NULL)
458	    abort();
459    }
460}
461
462int
463edit_entry(kadm5_principal_ent_t ent, int *mask,
464	   kadm5_principal_ent_t default_ent, int default_mask)
465{
466
467    set_defaults(ent, mask, default_ent, default_mask);
468
469    if(edit_deltat ("Max ticket life", &ent->max_life, mask,
470		    KADM5_MAX_LIFE) != 0)
471	return 1;
472
473    if(edit_deltat ("Max renewable life", &ent->max_renewable_life, mask,
474		    KADM5_MAX_RLIFE) != 0)
475	return 1;
476
477    if(edit_timet ("Principal expiration time", &ent->princ_expire_time, mask,
478		   KADM5_PRINC_EXPIRE_TIME) != 0)
479	return 1;
480
481    if(edit_timet ("Password expiration time", &ent->pw_expiration, mask,
482		   KADM5_PW_EXPIRATION) != 0)
483	return 1;
484
485    if(edit_attributes ("Attributes", &ent->attributes, mask,
486			KADM5_ATTRIBUTES) != 0)
487	return 1;
488
489    if(edit_policy ("Policy", &ent->policy, mask,
490			KADM5_POLICY) != 0)
491	return 1;
492
493    return 0;
494}
495
496/*
497 * Parse the arguments, set the fields in `ent' and the `mask' for the
498 * entries having been set.
499 * Return 1 on failure and 0 on success.
500 */
501
502int
503set_entry(krb5_context contextp,
504	  kadm5_principal_ent_t ent,
505	  int *mask,
506	  const char *max_ticket_life,
507	  const char *max_renewable_life,
508	  const char *expiration,
509	  const char *pw_expiration,
510	  const char *attributes,
511	  const char *policy)
512{
513    if (max_ticket_life != NULL) {
514	if (parse_deltat (max_ticket_life, &ent->max_life,
515			  mask, KADM5_MAX_LIFE)) {
516	    krb5_warnx (contextp, "unable to parse `%s'", max_ticket_life);
517	    return 1;
518	}
519    }
520    if (max_renewable_life != NULL) {
521	if (parse_deltat (max_renewable_life, &ent->max_renewable_life,
522			  mask, KADM5_MAX_RLIFE)) {
523	    krb5_warnx (contextp, "unable to parse `%s'", max_renewable_life);
524	    return 1;
525	}
526    }
527
528    if (expiration) {
529	if (parse_timet (expiration, &ent->princ_expire_time,
530			mask, KADM5_PRINC_EXPIRE_TIME)) {
531	    krb5_warnx (contextp, "unable to parse `%s'", expiration);
532	    return 1;
533	}
534    }
535    if (pw_expiration) {
536	if (parse_timet (pw_expiration, &ent->pw_expiration,
537			 mask, KADM5_PW_EXPIRATION)) {
538	    krb5_warnx (contextp, "unable to parse `%s'", pw_expiration);
539	    return 1;
540	}
541    }
542    if (attributes != NULL) {
543	if (parse_attributes (attributes, &ent->attributes,
544			      mask, KADM5_ATTRIBUTES)) {
545	    krb5_warnx (contextp, "unable to parse `%s'", attributes);
546	    return 1;
547	}
548    }
549    if (policy != NULL) {
550	if (parse_policy (policy, &ent->policy,
551			      mask, KADM5_POLICY)) {
552	    krb5_warnx (contextp, "unable to parse `%s'", policy);
553	    return 1;
554	}
555    }
556    return 0;
557}
558
559/*
560 * Does `string' contain any globing characters?
561 */
562
563static int
564is_expression(const char *string)
565{
566    const char *p;
567    int quote = 0;
568
569    for(p = string; *p; p++) {
570	if(quote) {
571	    quote = 0;
572	    continue;
573	}
574	if(*p == '\\')
575	    quote++;
576	else if(strchr("[]*?", *p) != NULL)
577	    return 1;
578    }
579    return 0;
580}
581
582/*
583 * Loop over all principals matching exp.  If any of calls to `func'
584 * failes, the first error is returned when all principals are
585 * processed.
586 */
587int
588foreach_principal(const char *exp_str,
589		  int (*func)(krb5_principal, void*),
590		  const char *funcname,
591		  void *data)
592{
593    char **princs = NULL;
594    int num_princs = 0;
595    int i;
596    krb5_error_code saved_ret = 0, ret = 0;
597    krb5_principal princ_ent;
598    int is_expr;
599
600    /* if this isn't an expression, there is no point in wading
601       through the whole database looking for matches */
602    is_expr = is_expression(exp_str);
603    if(is_expr)
604	ret = kadm5_get_principals(kadm_handle, exp_str, &princs, &num_princs);
605    if(!is_expr || ret == KADM5_AUTH_LIST) {
606	/* we might be able to perform the requested opreration even
607           if we're not allowed to list principals */
608	num_princs = 1;
609	princs = malloc(sizeof(*princs));
610	if(princs == NULL)
611	    return ENOMEM;
612	princs[0] = strdup(exp_str);
613	if(princs[0] == NULL){
614	    free(princs);
615	    return ENOMEM;
616	}
617    } else if(ret) {
618	krb5_warn(context, ret, "kadm5_get_principals");
619	return ret;
620    }
621    for(i = 0; i < num_princs; i++) {
622	ret = krb5_parse_name(context, princs[i], &princ_ent);
623	if(ret){
624	    krb5_warn(context, ret, "krb5_parse_name(%s)", princs[i]);
625	    continue;
626	}
627	ret = (*func)(princ_ent, data);
628	if(ret) {
629	    krb5_clear_error_message(context);
630	    krb5_warn(context, ret, "%s %s", funcname, princs[i]);
631	    if (saved_ret == 0)
632		saved_ret = ret;
633	}
634	krb5_free_principal(context, princ_ent);
635    }
636    if (ret == 0 && saved_ret != 0)
637	ret = saved_ret;
638    kadm5_free_name_list(kadm_handle, princs, &num_princs);
639    return ret;
640}
641
642/*
643 * prompt with `prompt' and default value `def', and store the reply
644 * in `buf, len'
645 */
646
647static void interrupt(int sig) __attribute__((__noreturn__));
648
649
650#include <setjmp.h>
651
652static jmp_buf jmpbuf;
653
654static void
655interrupt(int sig)
656{
657    longjmp(jmpbuf, 1);
658}
659
660static int
661get_response(const char *prompt, const char *def, char *buf, size_t len)
662{
663    char *p;
664    void (*osig)(int);
665
666    osig = signal(SIGINT, interrupt);
667    if(setjmp(jmpbuf)) {
668	signal(SIGINT, osig);
669	fprintf(stderr, "\n");
670	return 1;
671    }
672
673    fprintf(stderr, "%s [%s]:", prompt, def);
674    if(fgets(buf, (int)len, stdin) == NULL) {
675	int save_errno = errno;
676	if(ferror(stdin))
677	    krb5_err(context, 1, save_errno, "<stdin>");
678	signal(SIGINT, osig);
679	return 1;
680    }
681    p = strchr(buf, '\n');
682    if(p)
683	*p = '\0';
684    if(strcmp(buf, "") == 0)
685	strlcpy(buf, def, len);
686    signal(SIGINT, osig);
687    return 0;
688}
689
690/*
691 * return [0, 16) or -1
692 */
693
694static int
695hex2n (char c)
696{
697    static char hexdigits[] = "0123456789abcdef";
698    const char *p;
699
700    p = strchr (hexdigits, tolower((unsigned char)c));
701    if (p == NULL)
702	return -1;
703    else
704	return (int)(p - hexdigits);
705}
706
707/*
708 * convert a key in a readable format into a keyblock.
709 * return 0 iff succesful, otherwise `err' should point to an error message
710 */
711
712int
713parse_des_key (const char *key_string, krb5_key_data *key_data,
714	       const char **error)
715{
716    const char *p = key_string;
717    unsigned char bits[8];
718    int i;
719
720    if (strlen (key_string) != 16) {
721	*error = "bad length, should be 16 for DES key";
722	return 1;
723    }
724    for (i = 0; i < 8; ++i) {
725	int d1, d2;
726
727	d1 = hex2n(p[2 * i]);
728	d2 = hex2n(p[2 * i + 1]);
729	if (d1 < 0 || d2 < 0) {
730	    *error = "non-hex character";
731	    return 1;
732	}
733	bits[i] = (d1 << 4) | d2;
734    }
735    for (i = 0; i < 3; ++i) {
736	key_data[i].key_data_ver  = 2;
737	key_data[i].key_data_kvno = 0;
738	/* key */
739	key_data[i].key_data_type[0]     = ETYPE_DES_CBC_CRC;
740	key_data[i].key_data_length[0]   = 8;
741	key_data[i].key_data_contents[0] = malloc(8);
742	if (key_data[i].key_data_contents[0] == NULL) {
743	    *error = "malloc";
744	    return ENOMEM;
745	}
746	memcpy (key_data[i].key_data_contents[0], bits, 8);
747	/* salt */
748	key_data[i].key_data_type[1]     = KRB5_PW_SALT;
749	key_data[i].key_data_length[1]   = 0;
750	key_data[i].key_data_contents[1] = NULL;
751    }
752    key_data[0].key_data_type[0] = ETYPE_DES_CBC_MD5;
753    key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
754    return 0;
755}
756