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 RCSID("$Id: get.c 21745 2007-07-31 16:11:25Z lha $"); 40 41 static 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, "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 { 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 != k->key_data_type[1]) 116 return 1; 117 if (salt->saltvalue.length != 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 158 strlcat(buf, ")", buf_len); 159 } 160 161 static void 162 format_field(kadm5_principal_ent_t princ, unsigned int field, 163 unsigned int subfield, char *buf, size_t buf_len, int condensed) 164 { 165 switch(field) { 166 case KADM5_PRINCIPAL: 167 if(condensed) 168 krb5_unparse_name_fixed_short(context, princ->principal, buf, buf_len); 169 else 170 krb5_unparse_name_fixed(context, princ->principal, buf, buf_len); 171 break; 172 173 case KADM5_PRINC_EXPIRE_TIME: 174 time_t2str(princ->princ_expire_time, buf, buf_len, !condensed); 175 break; 176 177 case KADM5_PW_EXPIRATION: 178 time_t2str(princ->pw_expiration, buf, buf_len, !condensed); 179 break; 180 181 case KADM5_LAST_PWD_CHANGE: 182 time_t2str(princ->last_pwd_change, buf, buf_len, !condensed); 183 break; 184 185 case KADM5_MAX_LIFE: 186 deltat2str(princ->max_life, buf, buf_len); 187 break; 188 189 case KADM5_MAX_RLIFE: 190 deltat2str(princ->max_renewable_life, buf, buf_len); 191 break; 192 193 case KADM5_MOD_TIME: 194 time_t2str(princ->mod_date, buf, buf_len, !condensed); 195 break; 196 197 case KADM5_MOD_NAME: 198 if (princ->mod_name == NULL) 199 strlcpy(buf, "unknown", buf_len); 200 else if(condensed) 201 krb5_unparse_name_fixed_short(context, princ->mod_name, buf, buf_len); 202 else 203 krb5_unparse_name_fixed(context, princ->mod_name, buf, buf_len); 204 break; 205 case KADM5_ATTRIBUTES: 206 attributes2str (princ->attributes, buf, buf_len); 207 break; 208 case KADM5_KVNO: 209 snprintf(buf, buf_len, "%d", princ->kvno); 210 break; 211 case KADM5_MKVNO: 212 snprintf(buf, buf_len, "%d", princ->mkvno); 213 break; 214 case KADM5_LAST_SUCCESS: 215 time_t2str(princ->last_success, buf, buf_len, !condensed); 216 break; 217 case KADM5_LAST_FAILED: 218 time_t2str(princ->last_failed, buf, buf_len, !condensed); 219 break; 220 case KADM5_FAIL_AUTH_COUNT: 221 snprintf(buf, buf_len, "%d", princ->fail_auth_count); 222 break; 223 case KADM5_POLICY: 224 if(princ->policy != NULL) 225 strlcpy(buf, princ->policy, buf_len); 226 else 227 strlcpy(buf, "none", buf_len); 228 break; 229 case KADM5_KEY_DATA:{ 230 krb5_salt def_salt; 231 int i; 232 char buf2[1024]; 233 krb5_get_pw_salt (context, princ->principal, &def_salt); 234 235 *buf = '\0'; 236 for (i = 0; i < princ->n_key_data; ++i) { 237 format_keytype(&princ->key_data[i], &def_salt, buf2, sizeof(buf2)); 238 if(i > 0) 239 strlcat(buf, ", ", buf_len); 240 strlcat(buf, buf2, buf_len); 241 } 242 krb5_free_salt (context, def_salt); 243 break; 244 } 245 case KADM5_TL_DATA: { 246 krb5_tl_data *tl; 247 248 for (tl = princ->tl_data; tl != NULL; tl = tl->tl_data_next) 249 if (tl->tl_data_type == subfield) 250 break; 251 if (tl == NULL) { 252 strlcpy(buf, "", buf_len); 253 break; 254 } 255 256 switch (subfield) { 257 case KRB5_TL_PASSWORD: 258 snprintf(buf, buf_len, "\"%.*s\"", 259 (int)tl->tl_data_length, 260 (const char *)tl->tl_data_contents); 261 break; 262 case KRB5_TL_PKINIT_ACL: { 263 HDB_Ext_PKINIT_acl acl; 264 size_t size; 265 int i, ret; 266 267 ret = decode_HDB_Ext_PKINIT_acl(tl->tl_data_contents, 268 tl->tl_data_length, 269 &acl, 270 &size); 271 if (ret) { 272 snprintf(buf, buf_len, "failed to decode ACL"); 273 break; 274 } 275 276 buf[0] = '\0'; 277 for (i = 0; i < acl.len; i++) { 278 strlcat(buf, "subject: ", buf_len); 279 strlcat(buf, acl.val[i].subject, buf_len); 280 if (acl.val[i].issuer) { 281 strlcat(buf, " issuer:", buf_len); 282 strlcat(buf, *acl.val[i].issuer, buf_len); 283 } 284 if (acl.val[i].anchor) { 285 strlcat(buf, " anchor:", buf_len); 286 strlcat(buf, *acl.val[i].anchor, buf_len); 287 } 288 if (i + 1 < acl.len) 289 strlcat(buf, ", ", buf_len); 290 } 291 free_HDB_Ext_PKINIT_acl(&acl); 292 break; 293 } 294 case KRB5_TL_ALIASES: { 295 HDB_Ext_Aliases alias; 296 size_t size; 297 int i, ret; 298 299 ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, 300 tl->tl_data_length, 301 &alias, 302 &size); 303 if (ret) { 304 snprintf(buf, buf_len, "failed to decode alias"); 305 break; 306 } 307 buf[0] = '\0'; 308 for (i = 0; i < alias.aliases.len; i++) { 309 char *p; 310 ret = krb5_unparse_name(context, &alias.aliases.val[i], &p); 311 if (ret) 312 break; 313 if (i < 0) 314 strlcat(buf, " ", buf_len); 315 strlcat(buf, p, buf_len); 316 free(p); 317 } 318 free_HDB_Ext_Aliases(&alias); 319 break; 320 } 321 default: 322 snprintf(buf, buf_len, "unknown type %d", subfield); 323 break; 324 } 325 break; 326 } 327 default: 328 strlcpy(buf, "<unknown>", buf_len); 329 break; 330 } 331 } 332 333 static void 334 print_entry_short(struct get_entry_data *data, kadm5_principal_ent_t princ) 335 { 336 char buf[1024]; 337 struct field_info *f; 338 339 for(f = data->chead; f != NULL; f = f->next) { 340 format_field(princ, f->ff->fieldvalue, f->ff->subvalue, buf, sizeof(buf), 1); 341 rtbl_add_column_entry_by_id(data->table, f->ff->fieldvalue, buf); 342 } 343 } 344 345 static void 346 print_entry_long(struct get_entry_data *data, kadm5_principal_ent_t princ) 347 { 348 char buf[1024]; 349 struct field_info *f; 350 int width = 0; 351 352 for(f = data->chead; f != NULL; f = f->next) { 353 int w = strlen(f->header ? f->header : f->ff->def_longheader); 354 if(w > width) 355 width = w; 356 } 357 for(f = data->chead; f != NULL; f = f->next) { 358 format_field(princ, f->ff->fieldvalue, f->ff->subvalue, buf, sizeof(buf), 0); 359 printf("%*s: %s\n", width, f->header ? f->header : f->ff->def_longheader, buf); 360 } 361 printf("\n"); 362 } 363 364 static int 365 do_get_entry(krb5_principal principal, void *data) 366 { 367 kadm5_principal_ent_rec princ; 368 krb5_error_code ret; 369 struct get_entry_data *e = data; 370 371 memset(&princ, 0, sizeof(princ)); 372 ret = kadm5_get_principal(kadm_handle, principal, 373 &princ, 374 e->mask | e->extra_mask); 375 if(ret) 376 return ret; 377 else { 378 (e->format)(e, &princ); 379 kadm5_free_principal_ent(kadm_handle, &princ); 380 } 381 return 0; 382 } 383 384 static void 385 free_columns(struct get_entry_data *data) 386 { 387 struct field_info *f, *next; 388 for(f = data->chead; f != NULL; f = next) { 389 free(f->header); 390 next = f->next; 391 free(f); 392 } 393 data->chead = NULL; 394 data->ctail = &data->chead; 395 } 396 397 static int 398 setup_columns(struct get_entry_data *data, const char *column_info) 399 { 400 char buf[1024], *q; 401 char *field, *header; 402 struct field_name *f; 403 404 while(strsep_copy(&column_info, ",", buf, sizeof(buf)) != -1) { 405 q = buf; 406 field = strsep(&q, "="); 407 header = strsep(&q, "="); 408 for(f = field_names; f->fieldname != NULL; f++) { 409 if(strcasecmp(field, f->fieldname) == 0) { 410 add_column(data, f, header); 411 break; 412 } 413 } 414 if(f->fieldname == NULL) { 415 krb5_warnx(context, "unknown field name \"%s\"", field); 416 free_columns(data); 417 return -1; 418 } 419 } 420 return 0; 421 } 422 423 #define DEFAULT_COLUMNS_SHORT "principal,princ_expire_time,pw_expiration,last_pwd_change,max_life,max_rlife" 424 #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" 425 #define DEFAULT_COLUMNS_TERSE "principal=" 426 427 static int 428 getit(struct get_options *opt, const char *name, int argc, char **argv) 429 { 430 int i; 431 krb5_error_code ret; 432 struct get_entry_data data; 433 434 if(opt->long_flag == -1 && (opt->short_flag == 1 || opt->terse_flag == 1)) 435 opt->long_flag = 0; 436 if(opt->short_flag == -1 && (opt->long_flag == 1 || opt->terse_flag == 1)) 437 opt->short_flag = 0; 438 if(opt->terse_flag == -1 && (opt->long_flag == 1 || opt->short_flag == 1)) 439 opt->terse_flag = 0; 440 if(opt->long_flag == 0 && opt->short_flag == 0 && opt->terse_flag == 0) 441 opt->short_flag = 1; 442 443 data.table = NULL; 444 data.chead = NULL; 445 data.ctail = &data.chead; 446 data.mask = 0; 447 data.extra_mask = 0; 448 449 if(opt->short_flag || opt->terse_flag) { 450 data.table = rtbl_create(); 451 rtbl_set_separator(data.table, " "); 452 data.format = print_entry_short; 453 } else 454 data.format = print_entry_long; 455 if(opt->column_info_string == NULL) { 456 if(opt->long_flag) 457 ret = setup_columns(&data, DEFAULT_COLUMNS_LONG); 458 else if(opt->short_flag) 459 ret = setup_columns(&data, DEFAULT_COLUMNS_SHORT); 460 else { 461 ret = setup_columns(&data, DEFAULT_COLUMNS_TERSE); 462 rtbl_set_flags(data.table, RTBL_HEADER_STYLE_NONE); 463 } 464 } else 465 ret = setup_columns(&data, opt->column_info_string); 466 467 if(ret != 0) { 468 if(data.table != NULL) 469 rtbl_destroy(data.table); 470 return 0; 471 } 472 473 for(i = 0; i < argc; i++) 474 ret = foreach_principal(argv[i], do_get_entry, "get", &data); 475 476 if(data.table != NULL) { 477 rtbl_format(data.table, stdout); 478 rtbl_destroy(data.table); 479 } 480 free_columns(&data); 481 return ret != 0; 482 } 483 484 int 485 get_entry(struct get_options *opt, int argc, char **argv) 486 { 487 return getit(opt, "get", argc, argv); 488 } 489 490 int 491 list_princs(struct list_options *opt, int argc, char **argv) 492 { 493 if(sizeof(struct get_options) != sizeof(struct list_options)) { 494 krb5_warnx(context, "programmer error: sizeof(struct get_options) != sizeof(struct list_options)"); 495 return 0; 496 } 497 return getit((struct get_options*)opt, "list", argc, argv); 498 } 499