xref: /freebsd/crypto/heimdal/kadmin/util.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /*
2  * Copyright (c) 1997, 1998, 1999 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 RCSID("$Id: util.c,v 1.23 1999/12/02 17:04:58 joda Exp $");
38 
39 /*
40  * util.c - functions for parsing, unparsing, and editing different
41  * types of data used in kadmin.
42  */
43 
44 /*
45  * attributes
46  */
47 
48 struct units kdb_attrs[] = {
49     { "new-princ",		KRB5_KDB_NEW_PRINC },
50     { "support-desmd5",		KRB5_KDB_SUPPORT_DESMD5 },
51     { "pwchange-service",	KRB5_KDB_PWCHANGE_SERVICE },
52     { "disallow-svr",		KRB5_KDB_DISALLOW_SVR },
53     { "requires-pw-change",	KRB5_KDB_REQUIRES_PWCHANGE },
54     { "requires-hw-auth",	KRB5_KDB_REQUIRES_HW_AUTH },
55     { "requires-pre-auth",	KRB5_KDB_REQUIRES_PRE_AUTH },
56     { "disallow-all-tix",	KRB5_KDB_DISALLOW_ALL_TIX },
57     { "disallow-dup-skey",	KRB5_KDB_DISALLOW_DUP_SKEY },
58     { "disallow-proxiable",	KRB5_KDB_DISALLOW_PROXIABLE },
59     { "disallow-renewable",	KRB5_KDB_DISALLOW_RENEWABLE },
60     { "disallow-tgt-based",	KRB5_KDB_DISALLOW_TGT_BASED },
61     { "disallow-forwardable",	KRB5_KDB_DISALLOW_FORWARDABLE },
62     { "disallow-postdated",	KRB5_KDB_DISALLOW_POSTDATED },
63     { NULL }
64 };
65 
66 /*
67  * convert the attributes in `attributes' into a printable string
68  * in `str, len'
69  */
70 
71 void
72 attributes2str(krb5_flags attributes, char *str, size_t len)
73 {
74     unparse_flags (attributes, kdb_attrs, str, len);
75 }
76 
77 /*
78  * convert the string in `str' into attributes in `flags'
79  * return 0 if parsed ok, else -1.
80  */
81 
82 int
83 str2attributes(const char *str, krb5_flags *flags)
84 {
85     int res;
86 
87     res = parse_flags (str, kdb_attrs, *flags);
88     if (res < 0)
89 	return res;
90     else {
91 	*flags = res;
92 	return 0;
93     }
94 }
95 
96 /*
97  * try to parse the string `resp' into attributes in `attr', also
98  * setting the `bit' in `mask' if attributes are given and valid.
99  */
100 
101 int
102 parse_attributes (const char *resp, krb5_flags *attr, int *mask, int bit)
103 {
104     krb5_flags tmp = *attr;
105 
106     if (resp[0] == '\0')
107 	return 0;
108     else if (str2attributes(resp, &tmp) == 0) {
109 	*attr = tmp;
110 	if (mask)
111 	    *mask |= bit;
112 	return 0;
113     } else if(*resp == '?') {
114 	print_flags_table (kdb_attrs, stderr);
115     } else {
116 	fprintf (stderr, "Unable to parse '%s'\n", resp);
117     }
118     return -1;
119 }
120 
121 /*
122  * allow the user to edit the attributes in `attr', prompting with `prompt'
123  */
124 
125 int
126 edit_attributes (const char *prompt, krb5_flags *attr, int *mask, int bit)
127 {
128     char buf[1024], resp[1024];
129 
130     if (mask && (*mask & bit))
131 	return 0;
132 
133     attributes2str(*attr, buf, sizeof(buf));
134     for (;;) {
135 	get_response("Attributes", buf, resp, sizeof(resp));
136 	if (parse_attributes (resp, attr, mask, bit) == 0)
137 	    break;
138     }
139     return 0;
140 }
141 
142 /*
143  * time_t
144  * the special value 0 means ``never''
145  */
146 
147 /*
148  * Convert the time `t' to a string representation in `str' (of max
149  * size `len').  If include_time also include time, otherwise just
150  * date.
151  */
152 
153 void
154 time_t2str(time_t t, char *str, size_t len, int include_time)
155 {
156     if(t) {
157 	if(include_time)
158 	    strftime(str, len, "%Y-%m-%d %H:%M:%S UTC", gmtime(&t));
159 	else
160 	    strftime(str, len, "%Y-%m-%d", gmtime(&t));
161     } else
162 	snprintf(str, len, "never");
163 }
164 
165 /*
166  * Convert the time representation in `str' to a time in `time'.
167  * Return 0 if succesful, else -1.
168  */
169 
170 int
171 str2time_t (const char *str, time_t *time)
172 {
173     const char *p;
174     struct tm tm;
175 
176     memset (&tm, 0, sizeof (tm));
177 
178     if(strcasecmp(str, "never") == 0) {
179 	*time = 0;
180 	return 0;
181     }
182 
183     p = strptime (str, "%Y-%m-%d", &tm);
184 
185     if (p == NULL)
186 	return -1;
187 
188     /* Do it on the end of the day */
189     tm.tm_hour = 23;
190     tm.tm_min  = 59;
191     tm.tm_sec  = 59;
192 
193     strptime (p, "%H:%M:%S", &tm);
194 
195     *time = tm2time (tm, 0);
196     return 0;
197 }
198 
199 /*
200  * try to parse the time in `resp' storing it in `value'
201  */
202 
203 int
204 parse_timet (const char *resp, krb5_timestamp *value, int *mask, int bit)
205 {
206     time_t tmp;
207 
208     if (str2time_t(resp, &tmp) == 0) {
209 	*value = tmp;
210 	if(mask)
211 	    *mask |= bit;
212 	return 0;
213     } else if(*resp == '?') {
214 	printf ("Print date on format YYYY-mm-dd [hh:mm:ss]\n");
215     } else {
216 	fprintf (stderr, "Unable to parse time '%s'\n", resp);
217     }
218     return -1;
219 }
220 
221 /*
222  * allow the user to edit the time in `value'
223  */
224 
225 int
226 edit_timet (const char *prompt, krb5_timestamp *value, int *mask, int bit)
227 {
228     char buf[1024], resp[1024];
229 
230     if (mask && (*mask & bit))
231 	return 0;
232 
233     time_t2str (*value, buf, sizeof (buf), 0);
234 
235     for (;;) {
236 	get_response(prompt, buf, resp, sizeof(resp));
237 	if (parse_timet (resp, value, mask, bit) == 0)
238 	    break;
239     }
240     return 0;
241 }
242 
243 /*
244  * deltat
245  * the special value 0 means ``unlimited''
246  */
247 
248 /*
249  * convert the delta_t value in `t' into a printable form in `str, len'
250  */
251 
252 void
253 deltat2str(unsigned t, char *str, size_t len)
254 {
255     if(t)
256 	unparse_time(t, str, len);
257     else
258 	snprintf(str, len, "unlimited");
259 }
260 
261 /*
262  * parse the delta value in `str', storing result in `*delta'
263  * return 0 if ok, else -1
264  */
265 
266 int
267 str2deltat(const char *str, krb5_deltat *delta)
268 {
269     int res;
270 
271     if(strcasecmp(str, "unlimited") == 0) {
272 	*delta = 0;
273 	return 0;
274     }
275     res = parse_time(str, "day");
276     if (res < 0)
277 	return res;
278     else {
279 	*delta = res;
280 	return 0;
281     }
282 }
283 
284 /*
285  * try to parse the string in `resp' into a deltad in `value'
286  * `mask' will get the bit `bit' set if a value was given.
287  */
288 
289 int
290 parse_deltat (const char *resp, krb5_deltat *value, int *mask, int bit)
291 {
292     krb5_deltat tmp;
293 
294     if (str2deltat(resp, &tmp) == 0) {
295 	*value = tmp;
296 	if (mask)
297 	    *mask |= bit;
298 	return 0;
299     } else if(*resp == '?') {
300 	print_time_table (stderr);
301     } else {
302 	fprintf (stderr, "Unable to parse time '%s'\n", resp);
303     }
304     return -1;
305 }
306 
307 /*
308  * allow the user to edit the deltat in `value'
309  */
310 
311 int
312 edit_deltat (const char *prompt, krb5_deltat *value, int *mask, int bit)
313 {
314     char buf[1024], resp[1024];
315 
316     if (mask && (*mask & bit))
317 	return 0;
318 
319     deltat2str(*value, buf, sizeof(buf));
320     for (;;) {
321 	get_response(prompt, buf, resp, sizeof(resp));
322 	if (parse_deltat (resp, value, mask, bit) == 0)
323 	    break;
324     }
325     return 0;
326 }
327 
328 /*
329  * allow the user to edit `ent'
330  */
331 
332 int
333 edit_entry(kadm5_principal_ent_t ent, int *mask,
334 	   kadm5_principal_ent_t default_ent, int default_mask)
335 {
336     if (default_ent && (default_mask & KADM5_MAX_LIFE))
337 	ent->max_life = default_ent->max_life;
338     edit_deltat ("Max ticket life", &ent->max_life, mask,
339 		 KADM5_MAX_LIFE);
340 
341     if (default_ent && (default_mask & KADM5_MAX_RLIFE))
342 	ent->max_renewable_life = default_ent->max_renewable_life;
343     edit_deltat ("Max renewable life", &ent->max_renewable_life, mask,
344 		 KADM5_MAX_RLIFE);
345 
346     if (default_ent && (default_mask & KADM5_PRINC_EXPIRE_TIME))
347 	ent->princ_expire_time = default_ent->princ_expire_time;
348     edit_timet ("Principal expiration time", &ent->princ_expire_time, mask,
349 	       KADM5_PRINC_EXPIRE_TIME);
350 
351     if (default_ent && (default_mask & KADM5_PW_EXPIRATION))
352 	ent->pw_expiration = default_ent->pw_expiration;
353     edit_timet ("Password expiration time", &ent->pw_expiration, mask,
354 	       KADM5_PW_EXPIRATION);
355 
356     if (default_ent && (default_mask & KADM5_ATTRIBUTES))
357 	ent->attributes = default_ent->attributes & ~KRB5_KDB_DISALLOW_ALL_TIX;
358     edit_attributes ("Attributes", &ent->attributes, mask,
359 		     KADM5_ATTRIBUTES);
360     return 0;
361 }
362 
363 /*
364  * Parse the arguments, set the fields in `ent' and the `mask' for the
365  * entries having been set.
366  * Return 1 on failure and 0 on success.
367  */
368 
369 int
370 set_entry(krb5_context context,
371 	  kadm5_principal_ent_t ent,
372 	  int *mask,
373 	  const char *max_ticket_life,
374 	  const char *max_renewable_life,
375 	  const char *expiration,
376 	  const char *pw_expiration,
377 	  const char *attributes)
378 {
379     if (max_ticket_life != NULL) {
380 	if (parse_deltat (max_ticket_life, &ent->max_life,
381 			  mask, KADM5_MAX_LIFE)) {
382 	    krb5_warnx (context, "unable to parse `%s'", max_ticket_life);
383 	    return 1;
384 	}
385     }
386     if (max_renewable_life != NULL) {
387 	if (parse_deltat (max_renewable_life, &ent->max_renewable_life,
388 			  mask, KADM5_MAX_RLIFE)) {
389 	    krb5_warnx (context, "unable to parse `%s'", max_renewable_life);
390 	    return 1;
391 	}
392     }
393 
394     if (expiration) {
395 	if (parse_timet (expiration, &ent->princ_expire_time,
396 			mask, KADM5_PRINC_EXPIRE_TIME)) {
397 	    krb5_warnx (context, "unable to parse `%s'", expiration);
398 	    return 1;
399 	}
400     }
401     if (pw_expiration) {
402 	if (parse_timet (pw_expiration, &ent->pw_expiration,
403 			 mask, KADM5_PW_EXPIRATION)) {
404 	    krb5_warnx (context, "unable to parse `%s'", pw_expiration);
405 	    return 1;
406 	}
407     }
408     if (attributes != NULL) {
409 	if (parse_attributes (attributes, &ent->attributes,
410 			      mask, KADM5_ATTRIBUTES)) {
411 	    krb5_warnx (context, "unable to parse `%s'", attributes);
412 	    return 1;
413 	}
414     }
415     return 0;
416 }
417 
418 /*
419  * Does `string' contain any globing characters?
420  */
421 
422 static int
423 is_expression(const char *string)
424 {
425     const char *p;
426     int quote = 0;
427 
428     for(p = string; *p; p++) {
429 	if(quote) {
430 	    quote = 0;
431 	    continue;
432 	}
433 	if(*p == '\\')
434 	    quote++;
435 	else if(strchr("[]*?", *p) != NULL)
436 	    return 1;
437     }
438     return 0;
439 }
440 
441 /* loop over all principals matching exp */
442 int
443 foreach_principal(const char *exp,
444 		  int (*func)(krb5_principal, void*),
445 		  void *data)
446 {
447     char **princs;
448     int num_princs;
449     int i;
450     krb5_error_code ret;
451     krb5_principal princ_ent;
452     int is_expr;
453 
454     /* if this isn't an expression, there is no point in wading
455        through the whole database looking for matches */
456     is_expr = is_expression(exp);
457     if(is_expr)
458 	ret = kadm5_get_principals(kadm_handle, exp, &princs, &num_princs);
459     if(!is_expr || ret == KADM5_AUTH_LIST) {
460 	/* we might be able to perform the requested opreration even
461            if we're not allowed to list principals */
462 	num_princs = 1;
463 	princs = malloc(sizeof(*princs));
464 	if(princs == NULL)
465 	    return ENOMEM;
466 	princs[0] = strdup(exp);
467 	if(princs[0] == NULL){
468 	    free(princs);
469 	    return ENOMEM;
470 	}
471     } else if(ret) {
472 	krb5_warn(context, ret, "kadm5_get_principals");
473 	return ret;
474     }
475     for(i = 0; i < num_princs; i++) {
476 	ret = krb5_parse_name(context, princs[i], &princ_ent);
477 	if(ret){
478 	    krb5_warn(context, ret, "krb5_parse_name(%s)", princs[i]);
479 	    continue;
480 	}
481 	ret = (*func)(princ_ent, data);
482 	if(ret) {
483 	    char *tmp;
484 	    krb5_error_code ret2;
485 
486 	    ret2 = krb5_unparse_name(context, princ_ent, &tmp);
487 	    if(ret2) {
488 		krb5_warn(context, ret2, "krb5_unparse_name");
489 		krb5_warn(context, ret, "<unknown principal>");
490 	    } else {
491 		krb5_warn(context, ret, "%s", tmp);
492 		free(tmp);
493 	    }
494 	}
495 	krb5_free_principal(context, princ_ent);
496     }
497     kadm5_free_name_list(kadm_handle, princs, &num_princs);
498     return 0;
499 }
500 
501 /*
502  * prompt with `prompt' and default value `def', and store the reply
503  * in `buf, len'
504  */
505 
506 void
507 get_response(const char *prompt, const char *def, char *buf, size_t len)
508 {
509     char *p;
510 
511     printf("%s [%s]:", prompt, def);
512     if(fgets(buf, len, stdin) == NULL)
513 	*buf = '\0';
514     p = strchr(buf, '\n');
515     if(p)
516 	*p = '\0';
517     if(strcmp(buf, "") == 0)
518 	strncpy(buf, def, len);
519     buf[len-1] = 0;
520 }
521