1 /* 2 * Copyright (c) 1997 - 2000 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.24 2000/01/02 03:58:45 assar 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); 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); 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 if(ret){ 131 kadm5_free_principal_ent(context->context, &ent); 132 memset(password, 0, strlen(password)); 133 free(password); 134 goto fail; 135 } 136 ret = kadm5_create_principal(kadm_handle, &ent, 137 mask, password); 138 kadm5_free_principal_ent(kadm_handle, &ent); 139 memset(password, 0, strlen(password)); 140 free(password); 141 krb5_storage_free(sp); 142 sp = krb5_storage_emem(); 143 krb5_store_int32(sp, ret); 144 break; 145 } 146 case kadm_modify:{ 147 op = "MODIFY"; 148 ret = kadm5_ret_principal_ent(sp, &ent); 149 if(ret) 150 goto fail; 151 ret = krb5_ret_int32(sp, &mask); 152 if(ret){ 153 kadm5_free_principal_ent(context, &ent); 154 goto fail; 155 } 156 krb5_unparse_name_fixed(context->context, ent.principal, 157 name, sizeof(name)); 158 krb5_warnx(context->context, "%s: %s %s", client, op, name); 159 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_MODIFY); 160 if(ret){ 161 kadm5_free_principal_ent(context, &ent); 162 goto fail; 163 } 164 ret = kadm5_modify_principal(kadm_handle, &ent, mask); 165 kadm5_free_principal_ent(kadm_handle, &ent); 166 krb5_storage_free(sp); 167 sp = krb5_storage_emem(); 168 krb5_store_int32(sp, ret); 169 break; 170 } 171 case kadm_rename:{ 172 op = "RENAME"; 173 ret = krb5_ret_principal(sp, &princ); 174 if(ret) 175 goto fail; 176 ret = krb5_ret_principal(sp, &princ2); 177 if(ret){ 178 krb5_free_principal(context->context, princ); 179 goto fail; 180 } 181 krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); 182 krb5_unparse_name_fixed(context->context, princ2, name2, sizeof(name2)); 183 krb5_warnx(context->context, "%s: %s %s -> %s", 184 client, op, name, name2); 185 ret = _kadm5_acl_check_permission(context, 186 KADM5_PRIV_ADD|KADM5_PRIV_DELETE); 187 if(ret){ 188 krb5_free_principal(context->context, princ); 189 goto fail; 190 } 191 ret = kadm5_rename_principal(kadm_handle, princ, princ2); 192 krb5_free_principal(context->context, princ); 193 krb5_free_principal(context->context, princ2); 194 krb5_storage_free(sp); 195 sp = krb5_storage_emem(); 196 krb5_store_int32(sp, ret); 197 break; 198 } 199 case kadm_chpass:{ 200 op = "CHPASS"; 201 ret = krb5_ret_principal(sp, &princ); 202 if(ret) 203 goto fail; 204 ret = krb5_ret_string(sp, &password); 205 if(ret){ 206 krb5_free_principal(context->context, princ); 207 goto fail; 208 } 209 krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); 210 krb5_warnx(context->context, "%s: %s %s", client, op, name); 211 212 /* 213 * The change is allowed if at least one of: 214 * a) it's for the principal him/herself and this was an initial ticket 215 * b) the user is on the CPW ACL. 216 */ 217 218 if (initial 219 && krb5_principal_compare (context->context, context->caller, 220 princ)) 221 ret = 0; 222 else 223 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW); 224 225 if(ret) { 226 krb5_free_principal(context->context, princ); 227 goto fail; 228 } 229 ret = kadm5_chpass_principal(kadm_handle, princ, password); 230 krb5_free_principal(context->context, princ); 231 memset(password, 0, strlen(password)); 232 free(password); 233 krb5_storage_free(sp); 234 sp = krb5_storage_emem(); 235 krb5_store_int32(sp, ret); 236 break; 237 } 238 case kadm_randkey:{ 239 op = "RANDKEY"; 240 ret = krb5_ret_principal(sp, &princ); 241 if(ret) 242 goto fail; 243 krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); 244 krb5_warnx(context->context, "%s: %s %s", client, op, name); 245 /* 246 * The change is allowed if at least one of: 247 * a) it's for the principal him/herself and this was an initial ticket 248 * b) the user is on the CPW ACL. 249 */ 250 251 if (initial 252 && krb5_principal_compare (context->context, context->caller, 253 princ)) 254 ret = 0; 255 else 256 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW); 257 258 if(ret) { 259 krb5_free_principal(context->context, princ); 260 goto fail; 261 } 262 ret = kadm5_randkey_principal(kadm_handle, princ, 263 &new_keys, &n_keys); 264 krb5_free_principal(context->context, princ); 265 krb5_storage_free(sp); 266 sp = krb5_storage_emem(); 267 krb5_store_int32(sp, ret); 268 if(ret == 0){ 269 int i; 270 krb5_store_int32(sp, n_keys); 271 for(i = 0; i < n_keys; i++){ 272 krb5_store_keyblock(sp, new_keys[i]); 273 krb5_free_keyblock_contents(context->context, &new_keys[i]); 274 } 275 } 276 break; 277 } 278 case kadm_get_privs:{ 279 ret = kadm5_get_privs(kadm_handle, &mask); 280 krb5_storage_free(sp); 281 sp = krb5_storage_emem(); 282 krb5_store_int32(sp, ret); 283 if(ret == 0) 284 krb5_store_int32(sp, mask); 285 break; 286 } 287 case kadm_get_princs:{ 288 op = "LIST"; 289 ret = krb5_ret_int32(sp, &tmp); 290 if(ret) 291 goto fail; 292 if(tmp){ 293 ret = krb5_ret_string(sp, &exp); 294 if(ret) 295 goto fail; 296 }else 297 exp = NULL; 298 krb5_warnx(context->context, "%s: %s %s", client, op, exp ? exp : "*"); 299 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_LIST); 300 if(ret){ 301 free(exp); 302 goto fail; 303 } 304 ret = kadm5_get_principals(kadm_handle, exp, &princs, &n_princs); 305 free(exp); 306 krb5_storage_free(sp); 307 sp = krb5_storage_emem(); 308 krb5_store_int32(sp, ret); 309 if(ret == 0){ 310 int i; 311 krb5_store_int32(sp, n_princs); 312 for(i = 0; i < n_princs; i++) 313 krb5_store_string(sp, princs[i]); 314 kadm5_free_name_list(kadm_handle, princs, &n_princs); 315 } 316 break; 317 } 318 default: 319 krb5_warnx(context->context, "%s: UNKNOWN OP %d", client, cmd); 320 krb5_storage_free(sp); 321 sp = krb5_storage_emem(); 322 krb5_store_int32(sp, KADM5_FAILURE); 323 break; 324 } 325 krb5_storage_to_data(sp, out); 326 krb5_storage_free(sp); 327 return 0; 328 fail: 329 krb5_warn(context->context, ret, "%s", op); 330 sp->seek(sp, 0, SEEK_SET); 331 krb5_store_int32(sp, ret); 332 krb5_storage_to_data(sp, out); 333 krb5_storage_free(sp); 334 return 0; 335 } 336 337 static void 338 v5_loop (krb5_context context, 339 krb5_auth_context ac, 340 krb5_boolean initial, 341 void *kadm_handle, 342 int fd) 343 { 344 krb5_error_code ret; 345 ssize_t n; 346 unsigned long len; 347 u_char tmp[4]; 348 struct iovec iov[2]; 349 krb5_data in, out, msg, reply; 350 351 for (;;) { 352 n = krb5_net_read(context, &fd, tmp, 4); 353 if (n < 0) 354 krb5_err (context, 1, errno, "krb5_net_read"); 355 if (n == 0) 356 exit (0); 357 _krb5_get_int (tmp, &len, 4); 358 359 ret = krb5_data_alloc(&in, len); 360 if (ret) 361 krb5_err (context, 1, ret, "krb5_data_alloc"); 362 363 n = krb5_net_read(context, &fd, in.data, in.length); 364 if (n == 0) 365 exit (0); 366 if(n < 0) 367 krb5_errx(context, 1, "read error: %d", errno); 368 ret = krb5_rd_priv(context, ac, &in, &out, NULL); 369 if (ret) 370 krb5_err(context, 1, ret, "krb5_rd_priv"); 371 krb5_data_free(&in); 372 kadmind_dispatch(kadm_handle, initial, &out, &msg); 373 krb5_data_free(&out); 374 ret = krb5_mk_priv(context, ac, &msg, &reply, NULL); 375 krb5_data_free(&msg); 376 if(ret) 377 krb5_err(context, 1, ret, "krb5_mk_priv"); 378 379 _krb5_put_int(tmp, reply.length, 4); 380 381 iov[0].iov_base = tmp; 382 iov[0].iov_len = 4; 383 iov[1].iov_base = reply.data; 384 iov[1].iov_len = reply.length; 385 n = writev(fd, iov, 2); 386 krb5_data_free(&reply); 387 if(n < 0) 388 krb5_err(context, 1, errno, "writev"); 389 if(n < iov[0].iov_len + iov[1].iov_len) 390 krb5_errx(context, 1, "short write"); 391 } 392 } 393 394 static krb5_boolean 395 match_appl_version(void *data, const char *appl_version) 396 { 397 unsigned minor; 398 if(sscanf(appl_version, "KADM0.%u", &minor) != 1) 399 return 0; 400 *(unsigned*)data = minor; 401 return 1; 402 } 403 404 static void 405 handle_v5(krb5_context context, 406 krb5_auth_context ac, 407 krb5_keytab keytab, 408 int len, 409 int fd) 410 { 411 krb5_error_code ret; 412 u_char version[sizeof(KRB5_SENDAUTH_VERSION)]; 413 krb5_ticket *ticket; 414 krb5_principal server; 415 char *client; 416 void *kadm_handle; 417 ssize_t n; 418 krb5_boolean initial; 419 420 unsigned kadm_version; 421 kadm5_config_params realm_params; 422 423 if (len != sizeof(KRB5_SENDAUTH_VERSION)) 424 krb5_errx(context, 1, "bad sendauth len %d", len); 425 n = krb5_net_read(context, &fd, version, len); 426 if (n < 0) 427 krb5_err (context, 1, errno, "reading sendauth version"); 428 if (n == 0) 429 krb5_errx (context, 1, "EOF reading sendauth version"); 430 if(memcmp(version, KRB5_SENDAUTH_VERSION, len) != 0) 431 krb5_errx(context, 1, "bad sendauth version %.8s", version); 432 433 ret = krb5_parse_name(context, KADM5_ADMIN_SERVICE, &server); 434 if (ret) 435 krb5_err (context, 1, ret, "krb5_parse_name %s", KADM5_ADMIN_SERVICE); 436 ret = krb5_recvauth_match_version(context, &ac, &fd, 437 match_appl_version, &kadm_version, 438 server, KRB5_RECVAUTH_IGNORE_VERSION, 439 keytab, &ticket); 440 if(ret == KRB5_KT_NOTFOUND) { 441 char *name; 442 krb5_unparse_name(context, server, &name); 443 krb5_errx(context, 1, "krb5_recvauth: %s (%s)", 444 krb5_get_err_text(context, ret), 445 name); 446 } 447 krb5_free_principal(context, server); 448 449 if(ret) 450 krb5_err(context, 1, ret, "krb5_recvauth"); 451 452 memset(&realm_params, 0, sizeof(realm_params)); 453 454 if(kadm_version == 1) { 455 krb5_data enc_data, params; 456 ret = krb5_read_message(context, &fd, &enc_data); 457 ret = krb5_rd_priv(context, ac, &enc_data, ¶ms, NULL); 458 krb5_data_free(&enc_data); 459 _kadm5_unmarshal_params(context, ¶ms, &realm_params); 460 } 461 462 initial = ticket->ticket.flags.initial; 463 ret = krb5_unparse_name(context, ticket->client, &client); 464 if (ret) 465 krb5_err (context, 1, ret, "krb5_unparse_name"); 466 krb5_free_ticket (context, ticket); 467 ret = kadm5_init_with_password_ctx(context, 468 client, 469 NULL, 470 KADM5_ADMIN_SERVICE, 471 &realm_params, 472 0, 0, 473 &kadm_handle); 474 if(ret) 475 krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 476 v5_loop (context, ac, initial, kadm_handle, fd); 477 } 478 479 krb5_error_code 480 kadmind_loop(krb5_context context, 481 krb5_auth_context ac, 482 krb5_keytab keytab, 483 int fd) 484 { 485 unsigned char tmp[4]; 486 ssize_t n; 487 unsigned long len; 488 489 n = krb5_net_read(context, &fd, tmp, 4); 490 if(n == 0) 491 exit(0); 492 if(n < 0) 493 krb5_errx(context, 1, "read error: %d", errno); 494 _krb5_get_int(tmp, &len, 4); 495 if(len > 0xffff && (len & 0xffff) == ('K' << 8) + 'A') { 496 len >>= 16; 497 #ifdef KRB4 498 handle_v4(context, len, fd); 499 #else 500 krb5_errx(context, 1, "packet appears to be version 4"); 501 #endif 502 } else { 503 handle_v5(context, ac, keytab, len, fd); 504 } 505 return 0; 506 } 507