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