1/* 2 * Copyright (c) 1997 - 2005 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 <krb5-private.h> 36 37static kadm5_ret_t 38kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, 39 krb5_data *in, krb5_data *out) 40{ 41 kadm5_ret_t ret; 42 int32_t cmd, mask, tmp; 43 kadm5_server_context *contextp = kadm_handlep; 44 char client[128], name[128], name2[128]; 45 const char *op = ""; 46 krb5_principal princ, princ2; 47 kadm5_principal_ent_rec ent; 48 char *password, *expression; 49 krb5_keyblock *new_keys; 50 krb5_key_salt_tuple *ks_tuple = NULL; 51 krb5_boolean keepold = FALSE; 52 uint32_t n_ks_tuple = 0; 53 int n_keys; 54 char **princs; 55 int n_princs; 56 int keys_ok = 0; 57 krb5_storage *sp; 58 59 krb5_unparse_name_fixed(contextp->context, contextp->caller, 60 client, sizeof(client)); 61 62 sp = krb5_storage_from_data(in); 63 if (sp == NULL) 64 krb5_errx(contextp->context, 1, "out of memory"); 65 66 krb5_ret_int32(sp, &cmd); 67 switch(cmd){ 68 case kadm_get:{ 69 op = "GET"; 70 ret = krb5_ret_principal(sp, &princ); 71 if(ret) 72 goto fail; 73 ret = krb5_ret_int32(sp, &mask); 74 if(ret){ 75 krb5_free_principal(contextp->context, princ); 76 goto fail; 77 } 78 mask |= KADM5_PRINCIPAL; 79 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); 80 krb5_warnx(contextp->context, "%s: %s %s", client, op, name); 81 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET_KEYS, princ); 82 if (ret == 0) 83 keys_ok = 1; 84 else 85 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ); 86 if(ret){ 87 krb5_free_principal(contextp->context, princ); 88 goto fail; 89 } 90 ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask); 91 krb5_storage_free(sp); 92 sp = krb5_storage_emem(); 93 krb5_store_int32(sp, ret); 94 if(ret == 0){ 95 if (keys_ok) 96 kadm5_store_principal_ent(sp, &ent); 97 else 98 kadm5_store_principal_ent_nokeys(sp, &ent); 99 kadm5_free_principal_ent(kadm_handlep, &ent); 100 } 101 krb5_free_principal(contextp->context, princ); 102 break; 103 } 104 case kadm_delete:{ 105 op = "DELETE"; 106 ret = krb5_ret_principal(sp, &princ); 107 if(ret) 108 goto fail; 109 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); 110 krb5_warnx(contextp->context, "%s: %s %s", client, op, name); 111 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ); 112 if(ret){ 113 krb5_free_principal(contextp->context, princ); 114 goto fail; 115 } 116 ret = kadm5_delete_principal(kadm_handlep, princ); 117 krb5_free_principal(contextp->context, princ); 118 krb5_storage_free(sp); 119 sp = krb5_storage_emem(); 120 krb5_store_int32(sp, ret); 121 break; 122 } 123 case kadm_create:{ 124 op = "CREATE"; 125 ret = kadm5_ret_principal_ent(sp, &ent); 126 if(ret) 127 goto fail; 128 ret = krb5_ret_int32(sp, &mask); 129 if(ret){ 130 kadm5_free_principal_ent(contextp->context, &ent); 131 goto fail; 132 } 133 ret = krb5_ret_string(sp, &password); 134 if(ret){ 135 kadm5_free_principal_ent(contextp->context, &ent); 136 goto fail; 137 } 138 ret = _kadm5_ret_ks_tuple(sp, &n_ks_tuple, &ks_tuple); 139 if (ret) { 140 memset(password, 0, strlen(password)); 141 kadm5_free_principal_ent(contextp->context, &ent); 142 free(password); 143 goto fail; 144 } 145 krb5_unparse_name_fixed(contextp->context, ent.principal, 146 name, sizeof(name)); 147 krb5_warnx(contextp->context, "%s: %s %s", client, op, name); 148 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, 149 ent.principal); 150 if(ret){ 151 kadm5_free_principal_ent(contextp->context, &ent); 152 memset(password, 0, strlen(password)); 153 free(password); 154 goto fail; 155 } 156 157 ret = kadm5_create_principal_2(kadm_handlep, &ent, 158 mask, 159 n_ks_tuple, ks_tuple, 160 password); 161 kadm5_free_principal_ent(kadm_handlep, &ent); 162 memset(password, 0, strlen(password)); 163 free(password); 164 krb5_storage_free(sp); 165 sp = krb5_storage_emem(); 166 krb5_store_int32(sp, ret); 167 break; 168 } 169 case kadm_modify:{ 170 op = "MODIFY"; 171 ret = kadm5_ret_principal_ent(sp, &ent); 172 if(ret) 173 goto fail; 174 ret = krb5_ret_int32(sp, &mask); 175 if(ret){ 176 kadm5_free_principal_ent(contextp, &ent); 177 goto fail; 178 } 179 krb5_unparse_name_fixed(contextp->context, ent.principal, 180 name, sizeof(name)); 181 krb5_warnx(contextp->context, "%s: %s %s", client, op, name); 182 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY, 183 ent.principal); 184 if(ret){ 185 kadm5_free_principal_ent(contextp, &ent); 186 goto fail; 187 } 188 ret = kadm5_modify_principal(kadm_handlep, &ent, mask); 189 kadm5_free_principal_ent(kadm_handlep, &ent); 190 krb5_storage_free(sp); 191 sp = krb5_storage_emem(); 192 krb5_store_int32(sp, ret); 193 break; 194 } 195 case kadm_rename:{ 196 op = "RENAME"; 197 ret = krb5_ret_principal(sp, &princ); 198 if(ret) 199 goto fail; 200 ret = krb5_ret_principal(sp, &princ2); 201 if(ret){ 202 krb5_free_principal(contextp->context, princ); 203 goto fail; 204 } 205 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); 206 krb5_unparse_name_fixed(contextp->context, princ2, name2, sizeof(name2)); 207 krb5_warnx(contextp->context, "%s: %s %s -> %s", 208 client, op, name, name2); 209 ret = _kadm5_acl_check_permission(contextp, 210 KADM5_PRIV_ADD, 211 princ2) 212 || _kadm5_acl_check_permission(contextp, 213 KADM5_PRIV_DELETE, 214 princ); 215 if(ret){ 216 krb5_free_principal(contextp->context, princ); 217 krb5_free_principal(contextp->context, princ2); 218 goto fail; 219 } 220 ret = kadm5_rename_principal(kadm_handlep, princ, princ2); 221 krb5_free_principal(contextp->context, princ); 222 krb5_free_principal(contextp->context, princ2); 223 krb5_storage_free(sp); 224 sp = krb5_storage_emem(); 225 krb5_store_int32(sp, ret); 226 break; 227 } 228 case kadm_chpass:{ 229 op = "CHPASS"; 230 ret = krb5_ret_principal(sp, &princ); 231 if (ret) 232 goto fail; 233 ret = krb5_ret_string(sp, &password); 234 if (ret) { 235 krb5_free_principal(contextp->context, princ); 236 goto fail; 237 } 238 ret = krb5_ret_int32(sp, &keepold); 239 if (ret && ret != HEIM_ERR_EOF) { 240 krb5_free_principal(contextp->context, princ); 241 goto fail; 242 } 243 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); 244 krb5_warnx(contextp->context, "%s: %s %s", client, op, name); 245 246 /* 247 * The change is allowed if at least one of: 248 * 249 * a) allowed by sysadmin 250 * b) it's for the principal him/herself and this was an 251 * initial ticket, but then, check with the password quality 252 * function. 253 * c) the user is on the CPW ACL. 254 */ 255 256 if (krb5_config_get_bool_default(contextp->context, NULL, TRUE, 257 "kadmin", "allow_self_change_password", NULL) 258 && initial 259 && krb5_principal_compare (contextp->context, contextp->caller, 260 princ)) 261 { 262 krb5_data pwd_data; 263 const char *pwd_reason; 264 265 pwd_data.data = password; 266 pwd_data.length = strlen(password); 267 268 pwd_reason = kadm5_check_password_quality (contextp->context, 269 princ, &pwd_data); 270 if (pwd_reason != NULL) 271 ret = KADM5_PASS_Q_DICT; 272 else 273 ret = 0; 274 } else 275 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); 276 277 if(ret) { 278 krb5_free_principal(contextp->context, princ); 279 memset(password, 0, strlen(password)); 280 free(password); 281 goto fail; 282 } 283 ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL, 284 password); 285 krb5_free_principal(contextp->context, princ); 286 memset(password, 0, strlen(password)); 287 free(password); 288 krb5_storage_free(sp); 289 sp = krb5_storage_emem(); 290 krb5_store_int32(sp, ret); 291 break; 292 } 293 case kadm_chpass_with_key:{ 294 int i; 295 krb5_key_data *key_data; 296 int n_key_data; 297 298 op = "CHPASS_WITH_KEY"; 299 ret = krb5_ret_principal(sp, &princ); 300 if(ret) 301 goto fail; 302 ret = krb5_ret_int32(sp, &n_key_data); 303 if (ret) { 304 krb5_free_principal(contextp->context, princ); 305 goto fail; 306 } 307 ret = krb5_ret_int32(sp, &keepold); 308 if (ret && ret != HEIM_ERR_EOF) { 309 krb5_free_principal(contextp->context, princ); 310 goto fail; 311 } 312 /* n_key_data will be squeezed into an int16_t below. */ 313 if (n_key_data < 0 || n_key_data >= 1 << 16 || 314 (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) { 315 ret = ERANGE; 316 krb5_free_principal(contextp->context, princ); 317 goto fail; 318 } 319 320 key_data = malloc (n_key_data * sizeof(*key_data)); 321 if (key_data == NULL && n_key_data != 0) { 322 ret = ENOMEM; 323 krb5_free_principal(contextp->context, princ); 324 goto fail; 325 } 326 327 for (i = 0; i < n_key_data; ++i) { 328 ret = kadm5_ret_key_data (sp, &key_data[i]); 329 if (ret) { 330 int16_t dummy = i; 331 332 kadm5_free_key_data (contextp, &dummy, key_data); 333 free (key_data); 334 krb5_free_principal(contextp->context, princ); 335 goto fail; 336 } 337 } 338 339 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); 340 krb5_warnx(contextp->context, "%s: %s %s", client, op, name); 341 342 /* 343 * The change is only allowed if the user is on the CPW ACL, 344 * this it to force password quality check on the user. 345 */ 346 347 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); 348 if(ret) { 349 int16_t dummy = n_key_data; 350 351 kadm5_free_key_data (contextp, &dummy, key_data); 352 free (key_data); 353 krb5_free_principal(contextp->context, princ); 354 goto fail; 355 } 356 ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold, 357 n_key_data, key_data); 358 { 359 int16_t dummy = n_key_data; 360 kadm5_free_key_data (contextp, &dummy, key_data); 361 } 362 free (key_data); 363 krb5_free_principal(contextp->context, princ); 364 krb5_storage_free(sp); 365 sp = krb5_storage_emem(); 366 krb5_store_int32(sp, ret); 367 break; 368 } 369 case kadm_randkey:{ 370 op = "RANDKEY"; 371 ret = krb5_ret_principal(sp, &princ); 372 if(ret) 373 goto fail; 374 krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); 375 krb5_warnx(contextp->context, "%s: %s %s", client, op, name); 376 /* 377 * The change is allowed if at least one of: 378 * a) it's for the principal him/herself and this was an initial ticket 379 * b) the user is on the CPW ACL. 380 */ 381 382 if (initial 383 && krb5_principal_compare (contextp->context, contextp->caller, 384 princ)) 385 ret = 0; 386 else 387 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); 388 389 if(ret) { 390 krb5_free_principal(contextp->context, princ); 391 goto fail; 392 } 393 394 /* 395 * See comments in kadm5_c_randkey_principal() regarding the 396 * protocol. 397 */ 398 ret = krb5_ret_int32(sp, &keepold); 399 if (ret != 0 && ret != HEIM_ERR_EOF) { 400 krb5_free_principal(contextp->context, princ); 401 goto fail; 402 } 403 ret = _kadm5_ret_ks_tuple(sp, &n_ks_tuple, &ks_tuple); 404 if (ret) { 405 krb5_free_principal(contextp->context, princ); 406 goto fail; 407 } 408 409 ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold, 410 n_ks_tuple, ks_tuple, &new_keys, 411 &n_keys); 412 krb5_free_principal(contextp->context, princ); 413 414 krb5_storage_free(sp); 415 sp = krb5_storage_emem(); 416 krb5_store_int32(sp, ret); 417 if(ret == 0){ 418 int i; 419 krb5_store_int32(sp, n_keys); 420 for(i = 0; i < n_keys; i++){ 421 krb5_store_keyblock(sp, new_keys[i]); 422 krb5_free_keyblock_contents(contextp->context, &new_keys[i]); 423 } 424 free(new_keys); 425 } 426 break; 427 } 428 case kadm_get_privs:{ 429 uint32_t privs; 430 ret = kadm5_get_privs(kadm_handlep, &privs); 431 krb5_storage_free(sp); 432 sp = krb5_storage_emem(); 433 krb5_store_int32(sp, ret); 434 if(ret == 0) 435 krb5_store_uint32(sp, privs); 436 break; 437 } 438 case kadm_get_princs:{ 439 op = "LIST"; 440 ret = krb5_ret_int32(sp, &tmp); 441 if(ret) 442 goto fail; 443 if(tmp){ 444 ret = krb5_ret_string(sp, &expression); 445 if(ret) 446 goto fail; 447 }else 448 expression = NULL; 449 krb5_warnx(contextp->context, "%s: %s %s", client, op, 450 expression ? expression : "*"); 451 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL); 452 if(ret){ 453 free(expression); 454 goto fail; 455 } 456 ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs); 457 free(expression); 458 krb5_storage_free(sp); 459 sp = krb5_storage_emem(); 460 krb5_store_int32(sp, ret); 461 if(ret == 0){ 462 int i; 463 krb5_store_int32(sp, n_princs); 464 for(i = 0; i < n_princs; i++) 465 krb5_store_string(sp, princs[i]); 466 kadm5_free_name_list(kadm_handlep, princs, &n_princs); 467 } 468 break; 469 } 470 default: 471 krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd); 472 krb5_storage_free(sp); 473 sp = krb5_storage_emem(); 474 krb5_store_int32(sp, KADM5_FAILURE); 475 break; 476 } 477 if (ks_tuple) 478 free(ks_tuple); 479 krb5_storage_to_data(sp, out); 480 krb5_storage_free(sp); 481 return 0; 482fail: 483 if (ks_tuple) 484 free(ks_tuple); 485 krb5_warn(contextp->context, ret, "%s", op); 486 krb5_storage_seek(sp, 0, SEEK_SET); 487 krb5_store_int32(sp, ret); 488 krb5_storage_to_data(sp, out); 489 krb5_storage_free(sp); 490 return 0; 491} 492 493static void 494v5_loop (krb5_context contextp, 495 krb5_auth_context ac, 496 krb5_boolean initial, 497 void *kadm_handlep, 498 krb5_socket_t fd) 499{ 500 krb5_error_code ret; 501 krb5_data in, out; 502 503 for (;;) { 504 doing_useful_work = 0; 505 if(term_flag) 506 exit(0); 507 ret = krb5_read_priv_message(contextp, ac, &fd, &in); 508 if(ret == HEIM_ERR_EOF) 509 exit(0); 510 if(ret) 511 krb5_err(contextp, 1, ret, "krb5_read_priv_message"); 512 doing_useful_work = 1; 513 kadmind_dispatch(kadm_handlep, initial, &in, &out); 514 krb5_data_free(&in); 515 ret = krb5_write_priv_message(contextp, ac, &fd, &out); 516 if(ret) 517 krb5_err(contextp, 1, ret, "krb5_write_priv_message"); 518 } 519} 520 521static krb5_boolean 522match_appl_version(const void *data, const char *appl_version) 523{ 524 unsigned minor; 525 if(sscanf(appl_version, "KADM0.%u", &minor) != 1) 526 return 0; 527 /*XXX*/ 528 *(unsigned*)(intptr_t)data = minor; 529 return 1; 530} 531 532static void 533handle_v5(krb5_context contextp, 534 krb5_keytab keytab, 535 krb5_socket_t fd) 536{ 537 krb5_error_code ret; 538 krb5_ticket *ticket; 539 char *server_name; 540 char *client; 541 void *kadm_handlep; 542 krb5_boolean initial; 543 krb5_auth_context ac = NULL; 544 545 unsigned kadm_version; 546 kadm5_config_params realm_params; 547 548 ret = krb5_recvauth_match_version(contextp, &ac, &fd, 549 match_appl_version, &kadm_version, 550 NULL, KRB5_RECVAUTH_IGNORE_VERSION, 551 keytab, &ticket); 552 if (ret) 553 krb5_err(contextp, 1, ret, "krb5_recvauth"); 554 555 ret = krb5_unparse_name (contextp, ticket->server, &server_name); 556 if (ret) 557 krb5_err (contextp, 1, ret, "krb5_unparse_name"); 558 559 if (strncmp (server_name, KADM5_ADMIN_SERVICE, 560 strlen(KADM5_ADMIN_SERVICE)) != 0) 561 krb5_errx (contextp, 1, "ticket for strange principal (%s)", 562 server_name); 563 564 free (server_name); 565 566 memset(&realm_params, 0, sizeof(realm_params)); 567 568 if(kadm_version == 1) { 569 krb5_data params; 570 ret = krb5_read_priv_message(contextp, ac, &fd, ¶ms); 571 if(ret) 572 krb5_err(contextp, 1, ret, "krb5_read_priv_message"); 573 _kadm5_unmarshal_params(contextp, ¶ms, &realm_params); 574 } 575 576 initial = ticket->ticket.flags.initial; 577 ret = krb5_unparse_name(contextp, ticket->client, &client); 578 if (ret) 579 krb5_err (contextp, 1, ret, "krb5_unparse_name"); 580 krb5_free_ticket (contextp, ticket); 581 ret = kadm5_s_init_with_password_ctx(contextp, 582 client, 583 NULL, 584 KADM5_ADMIN_SERVICE, 585 &realm_params, 586 0, 0, 587 &kadm_handlep); 588 if(ret) 589 krb5_err (contextp, 1, ret, "kadm5_init_with_password_ctx"); 590 v5_loop (contextp, ac, initial, kadm_handlep, fd); 591} 592 593krb5_error_code 594kadmind_loop(krb5_context contextp, 595 krb5_keytab keytab, 596 krb5_socket_t sock) 597{ 598 u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4]; 599 ssize_t n; 600 unsigned long len; 601 602 n = krb5_net_read(contextp, &sock, buf, 4); 603 if(n == 0) 604 exit(0); 605 if(n < 0) 606 krb5_err(contextp, 1, errno, "read"); 607 _krb5_get_int(buf, &len, 4); 608 609 if (len == sizeof(KRB5_SENDAUTH_VERSION)) { 610 611 n = krb5_net_read(contextp, &sock, buf + 4, len); 612 if (n < 0) 613 krb5_err (contextp, 1, errno, "reading sendauth version"); 614 if (n == 0) 615 krb5_errx (contextp, 1, "EOF reading sendauth version"); 616 617 if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) { 618 handle_v5(contextp, keytab, sock); 619 return 0; 620 } 621 len += 4; 622 } else 623 len = 4; 624 625 handle_mit(contextp, buf, len, sock); 626 627 return 0; 628} 629