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