1/* $NetBSD: get.c,v 1.5 2023/06/19 21:41:41 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997-2006 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "kadmin_locl.h" 37#include "kadmin-commands.h" 38#include <krb5/parse_units.h> 39#include <krb5/rtbl.h> 40 41static struct field_name { 42 const char *fieldname; 43 unsigned int fieldvalue; 44 unsigned int subvalue; 45 uint32_t extra_mask; 46 const char *default_header; 47 const char *def_longheader; 48 unsigned int flags; 49} field_names[] = { 50 { "principal", KADM5_PRINCIPAL, 0, 0, "Principal", "Principal", 0 }, 51 { "princ_expire_time", KADM5_PRINC_EXPIRE_TIME, 0, 0, "Expiration", "Principal expires", 0 }, 52 { "pw_expiration", KADM5_PW_EXPIRATION, 0, 0, "PW-exp", "Password expires", 0 }, 53 { "last_pwd_change", KADM5_LAST_PWD_CHANGE, 0, 0, "PW-change", "Last password change", 0 }, 54 { "max_life", KADM5_MAX_LIFE, 0, 0, "Max life", "Max ticket life", 0 }, 55 { "max_rlife", KADM5_MAX_RLIFE, 0, 0, "Max renew", "Max renewable life", 0 }, 56 { "mod_time", KADM5_MOD_TIME, 0, 0, "Mod time", "Last modified", 0 }, 57 { "mod_name", KADM5_MOD_NAME, 0, 0, "Modifier", "Modifier", 0 }, 58 { "attributes", KADM5_ATTRIBUTES, 0, 0, "Attributes", "Attributes", 0 }, 59 { "kvno", KADM5_KVNO, 0, 0, "Kvno", "Kvno", RTBL_ALIGN_RIGHT }, 60 { "mkvno", KADM5_MKVNO, 0, 0, "Mkvno", "Mkvno", RTBL_ALIGN_RIGHT }, 61 { "last_success", KADM5_LAST_SUCCESS, 0, 0, "Last login", "Last successful login", 0 }, 62 { "last_failed", KADM5_LAST_FAILED, 0, 0, "Last fail", "Last failed login", 0 }, 63 { "fail_auth_count", KADM5_FAIL_AUTH_COUNT, 0, 0, "Fail count", "Failed login count", RTBL_ALIGN_RIGHT }, 64 { "policy", KADM5_POLICY, 0, 0, "Policy", "Policy", 0 }, 65 { "keytypes", KADM5_KEY_DATA, 0, KADM5_PRINCIPAL | KADM5_KVNO, "Keytypes", "Keytypes", 0 }, 66 { "password", KADM5_TL_DATA, KRB5_TL_PASSWORD, KADM5_KEY_DATA, "Password", "Password", 0 }, 67 { "pkinit-acl", KADM5_TL_DATA, KRB5_TL_PKINIT_ACL, 0, "PK-INIT ACL", "PK-INIT ACL", 0 }, 68 { "aliases", KADM5_TL_DATA, KRB5_TL_ALIASES, 0, "Aliases", "Aliases", 0 }, 69 { "hist-kvno-diff-clnt", KADM5_TL_DATA, KRB5_TL_HIST_KVNO_DIFF_CLNT, 0, "Clnt hist keys", "Historic keys allowed for client", 0 }, 70 { "hist-kvno-diff-svc", KADM5_TL_DATA, KRB5_TL_HIST_KVNO_DIFF_SVC, 0, "Svc hist keys", "Historic keys allowed for service", 0 }, 71 { NULL, 0, 0, 0, NULL, NULL, 0 } 72}; 73 74struct field_info { 75 struct field_name *ff; 76 char *header; 77 struct field_info *next; 78}; 79 80struct get_entry_data { 81 void (*format)(struct get_entry_data*, kadm5_principal_ent_t); 82 rtbl_t table; 83 uint32_t mask; 84 uint32_t extra_mask; 85 struct field_info *chead, **ctail; 86}; 87 88static int 89add_column(struct get_entry_data *data, struct field_name *ff, const char *header) 90{ 91 struct field_info *f = malloc(sizeof(*f)); 92 if (f == NULL) 93 return ENOMEM; 94 f->ff = ff; 95 if(header) 96 f->header = strdup(header); 97 else 98 f->header = NULL; 99 f->next = NULL; 100 *data->ctail = f; 101 data->ctail = &f->next; 102 data->mask |= ff->fieldvalue; 103 data->extra_mask |= ff->extra_mask; 104 if(data->table != NULL) 105 rtbl_add_column_by_id(data->table, ff->fieldvalue, 106 header ? header : ff->default_header, ff->flags); 107 return 0; 108} 109 110/* 111 * return 0 iff `salt' actually is the same as the current salt in `k' 112 */ 113 114static int 115cmp_salt (const krb5_salt *salt, const krb5_key_data *k) 116{ 117 if (salt->salttype != (size_t)k->key_data_type[1]) 118 return 1; 119 if (salt->saltvalue.length != (size_t)k->key_data_length[1]) 120 return 1; 121 return memcmp (salt->saltvalue.data, k->key_data_contents[1], 122 salt->saltvalue.length); 123} 124 125static void 126format_keytype(krb5_key_data *k, krb5_salt *def_salt, char *buf, size_t buf_len) 127{ 128 krb5_error_code ret; 129 char *s; 130 int aret; 131 132 buf[0] = '\0'; 133 ret = krb5_enctype_to_string (context, 134 k->key_data_type[0], 135 &s); 136 if (ret) { 137 aret = asprintf (&s, "unknown(%d)", k->key_data_type[0]); 138 if (aret == -1) 139 return; /* Nothing to do here, we have no way to pass the err */ 140 } 141 strlcpy(buf, s, buf_len); 142 free(s); 143 144 strlcat(buf, "(", buf_len); 145 146 ret = krb5_salttype_to_string (context, 147 k->key_data_type[0], 148 k->key_data_type[1], 149 &s); 150 if (ret) { 151 aret = asprintf (&s, "unknown(%d)", k->key_data_type[1]); 152 if (aret == -1) 153 return; /* Again, nothing else to do... */ 154 } 155 strlcat(buf, s, buf_len); 156 free(s); 157 158 aret = 0; 159 if (cmp_salt(def_salt, k) == 0) 160 s = strdup(""); 161 else if(k->key_data_length[1] == 0) 162 s = strdup("()"); 163 else 164 aret = asprintf (&s, "(%.*s)", k->key_data_length[1], 165 (char *)k->key_data_contents[1]); 166 if (aret == -1 || s == NULL) 167 return; /* Again, nothing else we can do... */ 168 strlcat(buf, s, buf_len); 169 free(s); 170 aret = asprintf (&s, "[%d]", k->key_data_kvno); 171 if (aret == -1) 172 return; 173 strlcat(buf, ")", buf_len); 174 175 strlcat(buf, s, buf_len); 176 free(s); 177} 178 179static void 180format_field(kadm5_principal_ent_t princ, unsigned int field, 181 unsigned int subfield, char *buf, size_t buf_len, int condensed) 182{ 183 switch(field) { 184 case KADM5_PRINCIPAL: 185 if(condensed) 186 krb5_unparse_name_fixed_short(context, princ->principal, buf, buf_len); 187 else 188 krb5_unparse_name_fixed(context, princ->principal, buf, buf_len); 189 break; 190 191 case KADM5_PRINC_EXPIRE_TIME: 192 time_t2str(princ->princ_expire_time, buf, buf_len, !condensed); 193 break; 194 195 case KADM5_PW_EXPIRATION: 196 time_t2str(princ->pw_expiration, buf, buf_len, !condensed); 197 break; 198 199 case KADM5_LAST_PWD_CHANGE: 200 time_t2str(princ->last_pwd_change, buf, buf_len, !condensed); 201 break; 202 203 case KADM5_MAX_LIFE: 204 deltat2str(princ->max_life, buf, buf_len); 205 break; 206 207 case KADM5_MAX_RLIFE: 208 deltat2str(princ->max_renewable_life, buf, buf_len); 209 break; 210 211 case KADM5_MOD_TIME: 212 time_t2str(princ->mod_date, buf, buf_len, !condensed); 213 break; 214 215 case KADM5_MOD_NAME: 216 if (princ->mod_name == NULL) 217 strlcpy(buf, "unknown", buf_len); 218 else if(condensed) 219 krb5_unparse_name_fixed_short(context, princ->mod_name, buf, buf_len); 220 else 221 krb5_unparse_name_fixed(context, princ->mod_name, buf, buf_len); 222 break; 223 case KADM5_ATTRIBUTES: 224 attributes2str (princ->attributes, buf, buf_len); 225 break; 226 case KADM5_KVNO: 227 snprintf(buf, buf_len, "%d", princ->kvno); 228 break; 229 case KADM5_MKVNO: 230 /* XXX libkadm5srv decrypts the keys, so mkvno is always 0. */ 231 strlcpy(buf, "unknown", buf_len); 232 break; 233 case KADM5_LAST_SUCCESS: 234 time_t2str(princ->last_success, buf, buf_len, !condensed); 235 break; 236 case KADM5_LAST_FAILED: 237 time_t2str(princ->last_failed, buf, buf_len, !condensed); 238 break; 239 case KADM5_FAIL_AUTH_COUNT: 240 snprintf(buf, buf_len, "%d", princ->fail_auth_count); 241 break; 242 case KADM5_POLICY: 243 if(princ->policy != NULL) 244 strlcpy(buf, princ->policy, buf_len); 245 else 246 strlcpy(buf, "none", buf_len); 247 break; 248 case KADM5_KEY_DATA:{ 249 krb5_salt def_salt; 250 int i; 251 char buf2[1024]; 252 krb5_get_pw_salt (context, princ->principal, &def_salt); 253 254 *buf = '\0'; 255 for (i = 0; i < princ->n_key_data; ++i) { 256 format_keytype(&princ->key_data[i], &def_salt, buf2, sizeof(buf2)); 257 if(i > 0) 258 strlcat(buf, ", ", buf_len); 259 strlcat(buf, buf2, buf_len); 260 } 261 krb5_free_salt (context, def_salt); 262 break; 263 } 264 case KADM5_TL_DATA: { 265 krb5_tl_data *tl; 266 267 for (tl = princ->tl_data; tl != NULL; tl = tl->tl_data_next) 268 if ((unsigned)tl->tl_data_type == subfield) 269 break; 270 if (tl == NULL) { 271 strlcpy(buf, "", buf_len); 272 break; 273 } 274 275 switch (subfield) { 276 case KRB5_TL_PASSWORD: 277 snprintf(buf, buf_len, "\"%.*s\"", 278 (int)tl->tl_data_length, 279 (const char *)tl->tl_data_contents); 280 break; 281 case KRB5_TL_PKINIT_ACL: { 282 HDB_Ext_PKINIT_acl acl; 283 size_t size; 284 int ret; 285 size_t i; 286 287 ret = decode_HDB_Ext_PKINIT_acl(tl->tl_data_contents, 288 tl->tl_data_length, 289 &acl, 290 &size); 291 if (ret) { 292 snprintf(buf, buf_len, "failed to decode ACL"); 293 break; 294 } 295 296 buf[0] = '\0'; 297 for (i = 0; i < acl.len; i++) { 298 strlcat(buf, "subject: ", buf_len); 299 strlcat(buf, acl.val[i].subject, buf_len); 300 if (acl.val[i].issuer) { 301 strlcat(buf, " issuer:", buf_len); 302 strlcat(buf, *acl.val[i].issuer, buf_len); 303 } 304 if (acl.val[i].anchor) { 305 strlcat(buf, " anchor:", buf_len); 306 strlcat(buf, *acl.val[i].anchor, buf_len); 307 } 308 if (i + 1 < acl.len) 309 strlcat(buf, ", ", buf_len); 310 } 311 free_HDB_Ext_PKINIT_acl(&acl); 312 break; 313 } 314 case KRB5_TL_ALIASES: { 315 HDB_Ext_Aliases alias; 316 size_t size; 317 int ret; 318 size_t i; 319 320 ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, 321 tl->tl_data_length, 322 &alias, 323 &size); 324 if (ret) { 325 snprintf(buf, buf_len, "failed to decode alias"); 326 break; 327 } 328 buf[0] = '\0'; 329 for (i = 0; i < alias.aliases.len; i++) { 330 char *p; 331 ret = krb5_unparse_name(context, &alias.aliases.val[i], &p); 332 if (ret) 333 break; 334 if (i > 0) 335 strlcat(buf, " ", buf_len); 336 strlcat(buf, p, buf_len); 337 free(p); 338 } 339 free_HDB_Ext_Aliases(&alias); 340 break; 341 } 342 default: 343 snprintf(buf, buf_len, "unknown type %d", subfield); 344 break; 345 } 346 break; 347 } 348 default: 349 strlcpy(buf, "<unknown>", buf_len); 350 break; 351 } 352} 353 354static void 355print_entry_short(struct get_entry_data *data, kadm5_principal_ent_t princ) 356{ 357 char buf[1024]; 358 struct field_info *f; 359 360 for(f = data->chead; f != NULL; f = f->next) { 361 format_field(princ, f->ff->fieldvalue, f->ff->subvalue, buf, sizeof(buf), 1); 362 rtbl_add_column_entry_by_id(data->table, f->ff->fieldvalue, buf); 363 } 364} 365 366static void 367print_entry_long(struct get_entry_data *data, kadm5_principal_ent_t princ) 368{ 369 char buf[1024]; 370 struct field_info *f; 371 int width = 0; 372 373 for(f = data->chead; f != NULL; f = f->next) { 374 int w = strlen(f->header ? f->header : f->ff->def_longheader); 375 if(w > width) 376 width = w; 377 } 378 for(f = data->chead; f != NULL; f = f->next) { 379 format_field(princ, f->ff->fieldvalue, f->ff->subvalue, buf, sizeof(buf), 0); 380 printf("%*s: %s\n", width, f->header ? f->header : f->ff->def_longheader, buf); 381 } 382 printf("\n"); 383} 384 385static int 386do_get_entry(krb5_principal principal, void *data) 387{ 388 kadm5_principal_ent_rec princ; 389 krb5_error_code ret; 390 struct get_entry_data *e = data; 391 392 memset(&princ, 0, sizeof(princ)); 393 ret = kadm5_get_principal(kadm_handle, principal, 394 &princ, 395 e->mask | e->extra_mask); 396 if(ret) 397 return ret; 398 else { 399 (e->format)(e, &princ); 400 kadm5_free_principal_ent(kadm_handle, &princ); 401 } 402 return 0; 403} 404 405static void 406free_columns(struct get_entry_data *data) 407{ 408 struct field_info *f, *next; 409 for(f = data->chead; f != NULL; f = next) { 410 free(f->header); 411 next = f->next; 412 free(f); 413 } 414 data->chead = NULL; 415 data->ctail = &data->chead; 416} 417 418static int 419setup_columns(struct get_entry_data *data, const char *column_info) 420{ 421 char buf[1024], *q; 422 char *field, *header; 423 struct field_name *f; 424 425 while(strsep_copy(&column_info, ",", buf, sizeof(buf)) != -1) { 426 q = buf; 427 field = strsep(&q, "="); 428 header = strsep(&q, "="); 429 for(f = field_names; f->fieldname != NULL; f++) { 430 if(strcasecmp(field, f->fieldname) == 0) { 431 add_column(data, f, header); 432 break; 433 } 434 } 435 if(f->fieldname == NULL) { 436 krb5_warnx(context, "unknown field name \"%s\"", field); 437 free_columns(data); 438 return -1; 439 } 440 } 441 return 0; 442} 443 444static int 445do_list_entry(krb5_principal principal, void *data) 446{ 447 char buf[1024]; 448 krb5_error_code ret; 449 450 ret = krb5_unparse_name_fixed_short(context, principal, buf, sizeof(buf)); 451 if (ret != 0) 452 return ret; 453 printf("%s\n", buf); 454 return 0; 455} 456 457static int 458listit(const char *funcname, int argc, char **argv) 459{ 460 int i; 461 krb5_error_code ret, saved_ret = 0; 462 463 for (i = 0; i < argc; i++) { 464 ret = foreach_principal(argv[i], do_list_entry, funcname, NULL); 465 if (saved_ret == 0 && ret != 0) 466 saved_ret = ret; 467 } 468 return saved_ret != 0; 469} 470 471#define DEFAULT_COLUMNS_SHORT "principal,princ_expire_time,pw_expiration,last_pwd_change,max_life,max_rlife" 472#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" 473 474static int 475getit(struct get_options *opt, const char *name, int argc, char **argv) 476{ 477 int i; 478 krb5_error_code ret; 479 struct get_entry_data data; 480 481 if(opt->long_flag == -1 && (opt->short_flag == 1 || opt->terse_flag == 1)) 482 opt->long_flag = 0; 483 if(opt->short_flag == -1 && (opt->long_flag == 1 || opt->terse_flag == 1)) 484 opt->short_flag = 0; 485 if(opt->terse_flag == -1 && (opt->long_flag == 1 || opt->short_flag == 1)) 486 opt->terse_flag = 0; 487 if(opt->long_flag == 0 && opt->short_flag == 0 && opt->terse_flag == 0) 488 opt->short_flag = 1; 489 490 if (opt->terse_flag) 491 return listit(name, argc, argv); 492 493 data.table = NULL; 494 data.chead = NULL; 495 data.ctail = &data.chead; 496 data.mask = 0; 497 data.extra_mask = 0; 498 499 if(opt->short_flag) { 500 data.table = rtbl_create(); 501 rtbl_set_separator(data.table, " "); 502 data.format = print_entry_short; 503 } else 504 data.format = print_entry_long; 505 if(opt->column_info_string == NULL) { 506 if(opt->long_flag) 507 ret = setup_columns(&data, DEFAULT_COLUMNS_LONG); 508 else 509 ret = setup_columns(&data, DEFAULT_COLUMNS_SHORT); 510 } else 511 ret = setup_columns(&data, opt->column_info_string); 512 513 if(ret != 0) { 514 if(data.table != NULL) 515 rtbl_destroy(data.table); 516 return 0; 517 } 518 519 for(i = 0; i < argc; i++) 520 ret = foreach_principal(argv[i], do_get_entry, name, &data); 521 522 if(data.table != NULL) { 523 rtbl_format(data.table, stdout); 524 rtbl_destroy(data.table); 525 } 526 free_columns(&data); 527 return ret != 0; 528} 529 530int 531get_entry(struct get_options *opt, int argc, char **argv) 532{ 533 return getit(opt, "get", argc, argv); 534} 535 536int 537list_princs(struct list_options *opt, int argc, char **argv) 538{ 539 if(sizeof(struct get_options) != sizeof(struct list_options)) { 540 krb5_warnx(context, "programmer error: sizeof(struct get_options) != sizeof(struct list_options)"); 541 return 0; 542 } 543 return getit((struct get_options*)opt, "list", argc, argv); 544} 545