/ kadmin / get.c
get.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 "kadmin-commands.h"
 36  #include <parse_units.h>
 37  #include <rtbl.h>
 38  
 39  static struct field_name {
 40      const char *fieldname;
 41      unsigned int fieldvalue;
 42      unsigned int subvalue;
 43      uint32_t extra_mask;
 44      const char *default_header;
 45      const char *def_longheader;
 46      unsigned int flags;
 47  } field_names[] = {
 48      { "principal", KADM5_PRINCIPAL, 0, 0, "Principal", "Principal", 0 },
 49      { "princ_expire_time", KADM5_PRINC_EXPIRE_TIME, 0, 0, "Expiration", "Principal expires", 0 },
 50      { "pw_expiration", KADM5_PW_EXPIRATION, 0, 0, "PW-exp", "Password expires", 0 },
 51      { "last_pwd_change", KADM5_LAST_PWD_CHANGE, 0, 0, "PW-change", "Last password change", 0 },
 52      { "max_life", KADM5_MAX_LIFE, 0, 0, "Max life", "Max ticket life", 0 },
 53      { "max_rlife", KADM5_MAX_RLIFE, 0, 0, "Max renew", "Max renewable life", 0 },
 54      { "mod_time", KADM5_MOD_TIME, 0, 0, "Mod time", "Last modified", 0 },
 55      { "mod_name", KADM5_MOD_NAME, 0, 0, "Modifier", "Modifier", 0 },
 56      { "attributes", KADM5_ATTRIBUTES, 0, 0, "Attributes", "Attributes", 0 },
 57      { "kvno", KADM5_KVNO, 0, 0, "Kvno", "Kvno", RTBL_ALIGN_RIGHT  },
 58      { "mkvno", KADM5_MKVNO, 0, 0, "Mkvno", "Mkvno", RTBL_ALIGN_RIGHT },
 59      { "last_success", KADM5_LAST_SUCCESS, 0, 0, "Last login", "Last successful login", 0 },
 60      { "last_failed", KADM5_LAST_FAILED, 0, 0, "Last fail", "Last failed login", 0 },
 61      { "fail_auth_count", KADM5_FAIL_AUTH_COUNT, 0, 0, "Fail count", "Failed login count", RTBL_ALIGN_RIGHT },
 62      { "policy", KADM5_POLICY, 0, 0, "Policy", "Policy", 0 },
 63      { "keytypes", KADM5_KEY_DATA, 0, KADM5_PRINCIPAL | KADM5_KVNO, "Keytypes", "Keytypes", 0 },
 64      { "password", KADM5_TL_DATA, KRB5_TL_PASSWORD, KADM5_KEY_DATA, "Password", "Password", 0 },
 65      { "pkinit-acl", KADM5_TL_DATA, KRB5_TL_PKINIT_ACL, 0, "PK-INIT ACL", "PK-INIT ACL", 0 },
 66      { "aliases", KADM5_TL_DATA, KRB5_TL_ALIASES, 0, "Aliases", "Aliases", 0 },
 67      { "hist-kvno-diff-clnt", KADM5_TL_DATA, KRB5_TL_HIST_KVNO_DIFF_CLNT, 0, "Clnt hist keys", "Historic keys allowed for client", 0 },
 68      { "hist-kvno-diff-svc", KADM5_TL_DATA, KRB5_TL_HIST_KVNO_DIFF_SVC, 0, "Svc hist keys", "Historic keys allowed for service", 0 },
 69      { NULL }
 70  };
 71  
 72  struct field_info {
 73      struct field_name *ff;
 74      char *header;
 75      struct field_info *next;
 76  };
 77  
 78  struct get_entry_data {
 79      void (*format)(struct get_entry_data*, kadm5_principal_ent_t);
 80      rtbl_t table;
 81      uint32_t mask;
 82      uint32_t extra_mask;
 83      struct field_info *chead, **ctail;
 84  };
 85  
 86  static int
 87  add_column(struct get_entry_data *data, struct field_name *ff, const char *header)
 88  {
 89      struct field_info *f = malloc(sizeof(*f));
 90      if (f == NULL)
 91  	return ENOMEM;
 92      f->ff = ff;
 93      if(header)
 94  	f->header = strdup(header);
 95      else
 96  	f->header = NULL;
 97      f->next = NULL;
 98      *data->ctail = f;
 99      data->ctail = &f->next;
100      data->mask |= ff->fieldvalue;
101      data->extra_mask |= ff->extra_mask;
102      if(data->table != NULL)
103  	rtbl_add_column_by_id(data->table, ff->fieldvalue,
104  			      header ? header : ff->default_header, ff->flags);
105      return 0;
106  }
107  
108  /*
109   * return 0 iff `salt' actually is the same as the current salt in `k'
110   */
111  
112  static int
113  cmp_salt (const krb5_salt *salt, const krb5_key_data *k)
114  {
115      if (salt->salttype != (size_t)k->key_data_type[1])
116  	return 1;
117      if (salt->saltvalue.length != (size_t)k->key_data_length[1])
118  	return 1;
119      return memcmp (salt->saltvalue.data, k->key_data_contents[1],
120  		   salt->saltvalue.length);
121  }
122  
123  static void
124  format_keytype(krb5_key_data *k, krb5_salt *def_salt, char *buf, size_t buf_len)
125  {
126      krb5_error_code ret;
127      char *s;
128  
129      ret = krb5_enctype_to_string (context,
130  				  k->key_data_type[0],
131  				  &s);
132      if (ret)
133  	asprintf (&s, "unknown(%d)", k->key_data_type[0]);
134      strlcpy(buf, s, buf_len);
135      free(s);
136  
137      strlcat(buf, "(", buf_len);
138  
139      ret = krb5_salttype_to_string (context,
140  				   k->key_data_type[0],
141  				   k->key_data_type[1],
142  				   &s);
143      if (ret)
144  	asprintf (&s, "unknown(%d)", k->key_data_type[1]);
145      strlcat(buf, s, buf_len);
146      free(s);
147  
148      if (cmp_salt(def_salt, k) == 0)
149  	s = strdup("");
150      else if(k->key_data_length[1] == 0)
151  	s = strdup("()");
152      else
153  	asprintf (&s, "(%.*s)", k->key_data_length[1],
154  		  (char *)k->key_data_contents[1]);
155      strlcat(buf, s, buf_len);
156      free(s);
157      asprintf (&s, "[%d]", k->key_data_kvno);
158      strlcat(buf, ")", buf_len);
159  
160      strlcat(buf, s, buf_len);
161      free(s);
162  }
163  
164  static void
165  format_field(kadm5_principal_ent_t princ, unsigned int field,
166  	     unsigned int subfield, char *buf, size_t buf_len, int condensed)
167  {
168      switch(field) {
169      case KADM5_PRINCIPAL:
170  	if(condensed)
171  	    krb5_unparse_name_fixed_short(context, princ->principal, buf, buf_len);
172  	else
173  	    krb5_unparse_name_fixed(context, princ->principal, buf, buf_len);
174  	break;
175  
176      case KADM5_PRINC_EXPIRE_TIME:
177  	time_t2str(princ->princ_expire_time, buf, buf_len, !condensed);
178  	break;
179  
180      case KADM5_PW_EXPIRATION:
181  	time_t2str(princ->pw_expiration, buf, buf_len, !condensed);
182  	break;
183  
184      case KADM5_LAST_PWD_CHANGE:
185  	time_t2str(princ->last_pwd_change, buf, buf_len, !condensed);
186  	break;
187  
188      case KADM5_MAX_LIFE:
189  	deltat2str(princ->max_life, buf, buf_len);
190  	break;
191  
192      case KADM5_MAX_RLIFE:
193  	deltat2str(princ->max_renewable_life, buf, buf_len);
194  	break;
195  
196      case KADM5_MOD_TIME:
197  	time_t2str(princ->mod_date, buf, buf_len, !condensed);
198  	break;
199  
200      case KADM5_MOD_NAME:
201  	if (princ->mod_name == NULL)
202  	    strlcpy(buf, "unknown", buf_len);
203  	else if(condensed)
204  	    krb5_unparse_name_fixed_short(context, princ->mod_name, buf, buf_len);
205  	else
206  	    krb5_unparse_name_fixed(context, princ->mod_name, buf, buf_len);
207  	break;
208      case KADM5_ATTRIBUTES:
209  	attributes2str (princ->attributes, buf, buf_len);
210  	break;
211      case KADM5_KVNO:
212  	snprintf(buf, buf_len, "%d", princ->kvno);
213  	break;
214      case KADM5_MKVNO:
215  	/* XXX libkadm5srv decrypts the keys, so mkvno is always 0. */
216  	strlcpy(buf, "unknown", buf_len);
217  	break;
218      case KADM5_LAST_SUCCESS:
219  	time_t2str(princ->last_success, buf, buf_len, !condensed);
220  	break;
221      case KADM5_LAST_FAILED:
222  	time_t2str(princ->last_failed, buf, buf_len, !condensed);
223  	break;
224      case KADM5_FAIL_AUTH_COUNT:
225  	snprintf(buf, buf_len, "%d", princ->fail_auth_count);
226  	break;
227      case KADM5_POLICY:
228  	if(princ->policy != NULL)
229  	    strlcpy(buf, princ->policy, buf_len);
230  	else
231  	    strlcpy(buf, "none", buf_len);
232  	break;
233      case KADM5_KEY_DATA:{
234  	krb5_salt def_salt;
235  	int i;
236  	char buf2[1024];
237  	krb5_get_pw_salt (context, princ->principal, &def_salt);
238  
239  	*buf = '\0';
240  	for (i = 0; i < princ->n_key_data; ++i) {
241  	    format_keytype(&princ->key_data[i], &def_salt, buf2, sizeof(buf2));
242  	    if(i > 0)
243  		strlcat(buf, ", ", buf_len);
244  	    strlcat(buf, buf2, buf_len);
245  	}
246  	krb5_free_salt (context, def_salt);
247  	break;
248      }
249      case KADM5_TL_DATA: {
250  	krb5_tl_data *tl;
251  
252  	for (tl = princ->tl_data; tl != NULL; tl = tl->tl_data_next)
253  	    if ((unsigned)tl->tl_data_type == subfield)
254  		break;
255  	if (tl == NULL) {
256  	    strlcpy(buf, "", buf_len);
257  	    break;
258  	}
259  
260  	switch (subfield) {
261  	case KRB5_TL_PASSWORD:
262  	    snprintf(buf, buf_len, "\"%.*s\"",
263  		     (int)tl->tl_data_length,
264  		     (const char *)tl->tl_data_contents);
265  	    break;
266  	case KRB5_TL_PKINIT_ACL: {
267  	    HDB_Ext_PKINIT_acl acl;
268  	    size_t size;
269  	    int ret;
270  	    size_t i;
271  
272  	    ret = decode_HDB_Ext_PKINIT_acl(tl->tl_data_contents,
273  					    tl->tl_data_length,
274  					    &acl,
275  					    &size);
276  	    if (ret) {
277  		snprintf(buf, buf_len, "failed to decode ACL");
278  		break;
279  	    }
280  
281  	    buf[0] = '\0';
282  	    for (i = 0; i < acl.len; i++) {
283  		strlcat(buf, "subject: ", buf_len);
284  		strlcat(buf, acl.val[i].subject, buf_len);
285  		if (acl.val[i].issuer) {
286  		    strlcat(buf, " issuer:", buf_len);
287  		    strlcat(buf, *acl.val[i].issuer, buf_len);
288  		}
289  		if (acl.val[i].anchor) {
290  		    strlcat(buf, " anchor:", buf_len);
291  		    strlcat(buf, *acl.val[i].anchor, buf_len);
292  		}
293  		if (i + 1 < acl.len)
294  		    strlcat(buf, ", ", buf_len);
295  	    }
296  	    free_HDB_Ext_PKINIT_acl(&acl);
297  	    break;
298  	}
299  	case KRB5_TL_ALIASES: {
300  	    HDB_Ext_Aliases alias;
301  	    size_t size;
302  	    int ret;
303  	    size_t i;
304  
305  	    ret = decode_HDB_Ext_Aliases(tl->tl_data_contents,
306  					 tl->tl_data_length,
307  					 &alias,
308  					 &size);
309  	    if (ret) {
310  		snprintf(buf, buf_len, "failed to decode alias");
311  		break;
312  	    }
313  	    buf[0] = '\0';
314  	    for (i = 0; i < alias.aliases.len; i++) {
315  		char *p;
316  		ret = krb5_unparse_name(context, &alias.aliases.val[i], &p);
317  		if (ret)
318  		    break;
319  		if (i > 0)
320  		    strlcat(buf, " ", buf_len);
321  		strlcat(buf, p, buf_len);
322  		free(p);
323  	    }
324  	    free_HDB_Ext_Aliases(&alias);
325  	    break;
326  	}
327  	default:
328  	    snprintf(buf, buf_len, "unknown type %d", subfield);
329  	    break;
330  	}
331  	break;
332      }
333      default:
334  	strlcpy(buf, "<unknown>", buf_len);
335  	break;
336      }
337  }
338  
339  static void
340  print_entry_short(struct get_entry_data *data, kadm5_principal_ent_t princ)
341  {
342      char buf[1024];
343      struct field_info *f;
344  
345      for(f = data->chead; f != NULL; f = f->next) {
346  	format_field(princ, f->ff->fieldvalue, f->ff->subvalue, buf, sizeof(buf), 1);
347  	rtbl_add_column_entry_by_id(data->table, f->ff->fieldvalue, buf);
348      }
349  }
350  
351  static void
352  print_entry_long(struct get_entry_data *data, kadm5_principal_ent_t princ)
353  {
354      char buf[1024];
355      struct field_info *f;
356      size_t width = 0;
357  
358      for(f = data->chead; f != NULL; f = f->next) {
359  	size_t w = strlen(f->header ? f->header : f->ff->def_longheader);
360  	if(w > width)
361  	    width = w;
362      }
363      for(f = data->chead; f != NULL; f = f->next) {
364  	format_field(princ, f->ff->fieldvalue, f->ff->subvalue, buf, sizeof(buf), 0);
365  	printf("%*s: %s\n", (int)width, f->header ? f->header : f->ff->def_longheader, buf);
366      }
367      printf("\n");
368  }
369  
370  static int
371  do_get_entry(krb5_principal principal, void *data)
372  {
373      kadm5_principal_ent_rec princ;
374      krb5_error_code ret;
375      struct get_entry_data *e = data;
376  
377      memset(&princ, 0, sizeof(princ));
378      ret = kadm5_get_principal(kadm_handle, principal,
379  			      &princ,
380  			      e->mask | e->extra_mask);
381      if(ret)
382  	return ret;
383      else {
384  	(e->format)(e, &princ);
385  	kadm5_free_principal_ent(kadm_handle, &princ);
386      }
387      return 0;
388  }
389  
390  static void
391  free_columns(struct get_entry_data *data)
392  {
393      struct field_info *f, *next;
394      for(f = data->chead; f != NULL; f = next) {
395  	free(f->header);
396  	next = f->next;
397  	free(f);
398      }
399      data->chead = NULL;
400      data->ctail = &data->chead;
401  }
402  
403  static int
404  setup_columns(struct get_entry_data *data, const char *column_info)
405  {
406      char buf[1024], *q;
407      char *field, *header;
408      struct field_name *f;
409  
410      while(strsep_copy(&column_info, ",", buf, sizeof(buf)) != -1) {
411  	q = buf;
412  	field = strsep(&q, "=");
413  	header = strsep(&q, "=");
414  	for(f = field_names; f->fieldname != NULL; f++) {
415  	    if(strcasecmp(field, f->fieldname) == 0) {
416  		add_column(data, f, header);
417  		break;
418  	    }
419  	}
420  	if(f->fieldname == NULL) {
421  	    krb5_warnx(context, "unknown field name \"%s\"", field);
422  	    free_columns(data);
423  	    return -1;
424  	}
425      }
426      return 0;
427  }
428  
429  static int
430  do_list_entry(krb5_principal principal, void *data)
431  {
432      char buf[1024];
433      krb5_error_code ret;
434  
435      ret = krb5_unparse_name_fixed_short(context, principal, buf, sizeof(buf));
436      if (ret != 0)
437          return ret;
438      printf("%s\n", buf);
439      return 0;
440  }
441  
442  static int
443  listit(const char *funcname, int argc, char **argv)
444  {
445      int i;
446      krb5_error_code ret, saved_ret = 0;
447  
448      for (i = 0; i < argc; i++) {
449  	ret = foreach_principal(argv[i], do_list_entry, funcname, NULL);
450          if (saved_ret == 0 && ret != 0)
451              saved_ret = ret;
452      }
453      return saved_ret != 0;
454  }
455  
456  #define DEFAULT_COLUMNS_SHORT "principal,princ_expire_time,pw_expiration,last_pwd_change,max_life,max_rlife"
457  #define DEFAULT_COLUMNS_LONG "principal,princ_expire_time,pw_expiration,last_pwd_change,max_life,max_rlife,kvno,mkvno,last_success,last_failed,fail_auth_count,mod_time,mod_name,attributes,keytypes,pkinit-acl,aliases"
458  
459  static int
460  getit(struct get_options *opt, const char *name, int argc, char **argv)
461  {
462      int i;
463      krb5_error_code ret;
464      struct get_entry_data data;
465  
466      if(opt->long_flag == -1 && (opt->short_flag == 1 || opt->terse_flag == 1))
467  	opt->long_flag = 0;
468      if(opt->short_flag == -1 && (opt->long_flag == 1 || opt->terse_flag == 1))
469  	opt->short_flag = 0;
470      if(opt->terse_flag == -1 && (opt->long_flag == 1 || opt->short_flag == 1))
471  	opt->terse_flag = 0;
472      if(opt->long_flag == 0 && opt->short_flag == 0 && opt->terse_flag == 0)
473  	opt->short_flag = 1;
474  
475      if (opt->terse_flag)
476          return listit(name, argc, argv);
477  
478      data.table = NULL;
479      data.chead = NULL;
480      data.ctail = &data.chead;
481      data.mask = 0;
482      data.extra_mask = 0;
483  
484      if(opt->short_flag) {
485  	data.table = rtbl_create();
486  	rtbl_set_separator(data.table, "  ");
487  	data.format = print_entry_short;
488      } else
489  	data.format = print_entry_long;
490      if(opt->column_info_string == NULL) {
491  	if(opt->long_flag)
492  	    ret = setup_columns(&data, DEFAULT_COLUMNS_LONG);
493  	else
494  	    ret = setup_columns(&data, DEFAULT_COLUMNS_SHORT);
495      } else
496  	ret = setup_columns(&data, opt->column_info_string);
497  
498      if(ret != 0) {
499  	if(data.table != NULL)
500  	    rtbl_destroy(data.table);
501  	return 0;
502      }
503  
504      for(i = 0; i < argc; i++)
505  	ret = foreach_principal(argv[i], do_get_entry, name, &data);
506  
507      if(data.table != NULL) {
508  	rtbl_format(data.table, stdout);
509  	rtbl_destroy(data.table);
510      }
511      free_columns(&data);
512      return ret != 0;
513  }
514  
515  int
516  get_entry(struct get_options *opt, int argc, char **argv)
517  {
518      return getit(opt, "get", argc, argv);
519  }
520  
521  int
522  list_princs(struct list_options *opt, int argc, char **argv)
523  {
524      assert(sizeof(struct get_options) == sizeof(struct list_options));
525  
526      return getit((struct get_options*)opt, "list", argc, argv);
527  }