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 39static 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 72struct field_info { 73 struct field_name *ff; 74 char *header; 75 struct field_info *next; 76}; 77 78struct 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 86static int 87add_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 112static int 113cmp_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 123static void 124format_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 164static void 165format_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 339static void 340print_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 351static void 352print_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 370static int 371do_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 390static void 391free_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 403static int 404setup_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 429static int 430do_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 442static int 443listit(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 459static int 460getit(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 515int 516get_entry(struct get_options *opt, int argc, char **argv) 517{ 518 return getit(opt, "get", argc, argv); 519} 520 521int 522list_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} 528