/ kadmin / util.c
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  /*
 38   * util.c - functions for parsing, unparsing, and editing different
 39   * types of data used in kadmin.
 40   */
 41  
 42  static int
 43  get_response(const char *prompt, const char *def, char *buf, size_t len);
 44  
 45  /*
 46   * attributes
 47   */
 48  
 49  struct 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  
 76  void
 77  attributes2str(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  
 87  int
 88  str2attributes(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  
106  int
107  parse_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  
128  int
129  edit_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  
156  int
157  parse_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  
185  int
186  edit_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  
217  void
218  time_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  
234  int
235  str2time_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  
294  int
295  parse_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  
315  int
316  edit_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  
343  void
344  deltat2str(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  
357  int
358  str2deltat(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  
380  int
381  parse_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  
402  int
403  edit_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  
424  void
425  set_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  
462  int
463  edit_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  
502  int
503  set_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  
563  static int
564  is_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   */
587  int
588  foreach_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  
647  static void interrupt(int sig) __attribute__((__noreturn__));
648  
649  
650  #include <setjmp.h>
651  
652  static jmp_buf jmpbuf;
653  
654  static void
655  interrupt(int sig)
656  {
657      longjmp(jmpbuf, 1);
658  }
659  
660  static int
661  get_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  
694  static int
695  hex2n (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  
712  int
713  parse_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  }