1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 4 * 5 */ 6 7 /* 8 * Copyright (C) 1998 by the FundsXpress, INC. 9 * 10 * All rights reserved. 11 * 12 * Export of this software from the United States of America may require 13 * a specific license from the United States Government. It is the 14 * responsibility of any person or organization contemplating export to 15 * obtain such a license before exporting. 16 * 17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18 * distribute this software and its documentation for any purpose and 19 * without fee is hereby granted, provided that the above copyright 20 * notice appear in all copies and that both that copyright notice and 21 * this permission notice appear in supporting documentation, and that 22 * the name of FundsXpress. not be used in advertising or publicity pertaining 23 * to distribution of the software without specific, written prior 24 * permission. FundsXpress makes no representations about the suitability of 25 * this software for any purpose. It is provided "as is" without express 26 * or implied warranty. 27 * 28 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 29 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 30 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 31 */ 32 33 #include <k5-platform.h> 34 #include <errno.h> 35 #include <locale.h> 36 #include <stdio.h> 37 #include <signal.h> 38 #include <syslog.h> 39 #include <sys/types.h> 40 #ifdef _AIX 41 #include <sys/select.h> 42 #endif 43 #include <sys/time.h> 44 #include <sys/socket.h> 45 #include <unistd.h> 46 #include <netinet/in.h> 47 #include <netdb.h> 48 #include <gssrpc/rpc.h> 49 #include <gssapi/gssapi.h> 50 #include "gssapiP_krb5.h" /* for kg_get_context */ 51 #include <gssrpc/auth_gssapi.h> 52 #include <kadm5/admin.h> 53 #include <kadm5/kadm_rpc.h> 54 #include <adm_proto.h> 55 #include "kdb_kt.h" /* for krb5_ktkdb_set_context */ 56 #include <string.h> 57 #include <kdb_log.h> 58 59 #include "misc.h" 60 #include "auth.h" 61 62 #if defined(NEED_DAEMON_PROTO) 63 int daemon(int, int); 64 #endif 65 66 #define TIMEOUT 15 67 68 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL; 69 void *global_server_handle; 70 int nofork = 0; 71 char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL; 72 char *kprop = KPROPD_DEFAULT_KPROP; 73 char *dump_file = KPROP_DEFAULT_FILE; 74 char *kprop_port = NULL; 75 76 static krb5_context context; 77 static char *progname; 78 79 static void 80 usage() 81 { 82 fprintf(stderr, _("Usage: kadmind [-x db_args]* [-r realm] [-m] [-nofork] " 83 "[-port port-number]\n" 84 "\t\t[-proponly] [-p path-to-kdb5_util] [-F dump-file]\n" 85 "\t\t[-K path-to-kprop] [-k kprop-port] [-P pid_file]\n" 86 "\nwhere,\n\t[-x db_args]* - any number of database " 87 "specific arguments.\n" 88 "\t\t\tLook at each database documentation for " 89 "supported arguments\n")); 90 exit(1); 91 } 92 93 /* 94 * Output a message to stderr and the admin server log, and exit with status 1. 95 * msg should not be punctuated. If code is given, msg should indicate what 96 * operation was taking place in the present progressive. Otherwise msg should 97 * be capitalized and should indicate what went wrong. 98 */ 99 static void 100 fail_to_start(krb5_error_code code, const char *msg) 101 { 102 const char *errmsg; 103 104 if (code) { 105 errmsg = krb5_get_error_message(context, code); 106 fprintf(stderr, _("%s: %s while %s, aborting\n"), progname, errmsg, 107 msg); 108 krb5_klog_syslog(LOG_ERR, _("%s while %s, aborting\n"), errmsg, msg); 109 } else { 110 fprintf(stderr, _("%s: %s, aborting\n"), progname, msg); 111 krb5_klog_syslog(LOG_ERR, _("%s, aborting"), msg); 112 } 113 exit(1); 114 } 115 116 static int 117 write_pid_file(const char *pid_file) 118 { 119 FILE *file; 120 unsigned long pid; 121 int st1, st2; 122 123 file = fopen(pid_file, "w"); 124 if (file == NULL) 125 return errno; 126 pid = (unsigned long)getpid(); 127 st1 = (fprintf(file, "%ld\n", pid) < 0) ? errno : 0; 128 st2 = (fclose(file) == EOF) ? errno : 0; 129 return st1 ? st1 : st2; 130 } 131 132 /* Set up the main loop. If proponly is set, don't set up ports for kpasswd or 133 * kadmin. May set *ctx_out even on error. */ 134 static krb5_error_code 135 setup_loop(kadm5_config_params *params, int proponly, verto_ctx **ctx_out) 136 { 137 krb5_error_code ret; 138 verto_ctx *ctx; 139 140 *ctx_out = ctx = loop_init(VERTO_EV_TYPE_SIGNAL); 141 if (ctx == NULL) 142 return ENOMEM; 143 ret = loop_setup_signals(ctx, &global_server_handle, NULL); 144 if (ret) 145 return ret; 146 if (!proponly) { 147 ret = loop_add_udp_address(params->kpasswd_port, 148 params->kpasswd_listen); 149 if (ret) 150 return ret; 151 ret = loop_add_tcp_address(params->kpasswd_port, 152 params->kpasswd_listen); 153 if (ret) 154 return ret; 155 ret = loop_add_rpc_service(params->kadmind_port, 156 params->kadmind_listen, 157 KADM, KADMVERS, kadm_1); 158 if (ret) 159 return ret; 160 } 161 #ifndef DISABLE_IPROP 162 if (params->iprop_enabled) { 163 ret = loop_add_rpc_service(params->iprop_port, params->iprop_listen, 164 KRB5_IPROP_PROG, KRB5_IPROP_VERS, 165 krb5_iprop_prog_1); 166 if (ret) 167 return ret; 168 } 169 #endif 170 return loop_setup_network(ctx, &global_server_handle, progname, 171 DEFAULT_TCP_LISTEN_BACKLOG); 172 } 173 174 /* Point GSSAPI at the KDB keytab so we don't need an actual file keytab. */ 175 static krb5_error_code 176 setup_kdb_keytab() 177 { 178 krb5_error_code ret; 179 180 ret = krb5_ktkdb_set_context(context); 181 if (ret) 182 return ret; 183 ret = krb5_db_register_keytab(context); 184 if (ret) 185 return ret; 186 return krb5_gss_register_acceptor_identity("KDB:"); 187 } 188 189 190 /* Return "name@realm". */ 191 static char * 192 build_princ_name(char *name, char *realm) 193 { 194 char *fullname; 195 196 if (asprintf(&fullname, "%s@%s", name, realm) < 0) 197 return NULL; 198 return fullname; 199 } 200 201 /* Callback from GSSRPC for garbled/forged/replayed/etc messages. */ 202 static void 203 log_badverf(gss_name_t client_name, gss_name_t server_name, 204 struct svc_req *rqst, struct rpc_msg *msg, char *data) 205 { 206 static const struct { 207 rpcproc_t proc; 208 const char *proc_name; 209 } proc_names[] = { 210 {1, "CREATE_PRINCIPAL"}, 211 {2, "DELETE_PRINCIPAL"}, 212 {3, "MODIFY_PRINCIPAL"}, 213 {4, "RENAME_PRINCIPAL"}, 214 {5, "GET_PRINCIPAL"}, 215 {6, "CHPASS_PRINCIPAL"}, 216 {7, "CHRAND_PRINCIPAL"}, 217 {8, "CREATE_POLICY"}, 218 {9, "DELETE_POLICY"}, 219 {10, "MODIFY_POLICY"}, 220 {11, "GET_POLICY"}, 221 {12, "GET_PRIVS"}, 222 {13, "INIT"}, 223 {14, "GET_PRINCS"}, 224 {15, "GET_POLS"}, 225 {16, "SETKEY_PRINCIPAL"}, 226 /* 17 was "SETV4KEY_PRINCIPAL" */ 227 {18, "CREATE_PRINCIPAL3"}, 228 {19, "CHPASS_PRINCIPAL3"}, 229 {20, "CHRAND_PRINCIPAL3"}, 230 {21, "SETKEY_PRINCIPAL3"}, 231 {22, "PURGEKEYS"}, 232 {23, "GET_STRINGS"}, 233 {24, "SET_STRING"} 234 }; 235 OM_uint32 minor; 236 gss_buffer_desc client, server; 237 gss_OID gss_type; 238 const char *a; 239 rpcproc_t proc; 240 unsigned int i; 241 const char *procname; 242 size_t clen, slen; 243 char *cdots, *sdots; 244 245 client.length = 0; 246 client.value = NULL; 247 server.length = 0; 248 server.value = NULL; 249 250 (void)gss_display_name(&minor, client_name, &client, &gss_type); 251 (void)gss_display_name(&minor, server_name, &server, &gss_type); 252 if (client.value == NULL) { 253 client.value = "(null)"; 254 clen = sizeof("(null)") - 1; 255 } else { 256 clen = client.length; 257 } 258 trunc_name(&clen, &cdots); 259 if (server.value == NULL) { 260 server.value = "(null)"; 261 slen = sizeof("(null)") - 1; 262 } else { 263 slen = server.length; 264 } 265 trunc_name(&slen, &sdots); 266 a = client_addr(rqst->rq_xprt); 267 268 proc = msg->rm_call.cb_proc; 269 procname = NULL; 270 for (i = 0; i < sizeof(proc_names) / sizeof(*proc_names); i++) { 271 if (proc_names[i].proc == proc) { 272 procname = proc_names[i].proc_name; 273 break; 274 } 275 } 276 if (procname != NULL) { 277 krb5_klog_syslog(LOG_NOTICE, 278 _("WARNING! Forged/garbled request: %s, claimed " 279 "client = %.*s%s, server = %.*s%s, addr = %s"), 280 procname, (int)clen, (char *)client.value, cdots, 281 (int)slen, (char *)server.value, sdots, a); 282 } else { 283 krb5_klog_syslog(LOG_NOTICE, 284 _("WARNING! Forged/garbled request: %d, claimed " 285 "client = %.*s%s, server = %.*s%s, addr = %s"), 286 proc, (int)clen, (char *)client.value, cdots, 287 (int)slen, (char *)server.value, sdots, a); 288 } 289 290 (void)gss_release_buffer(&minor, &client); 291 (void)gss_release_buffer(&minor, &server); 292 } 293 294 /* Callback from GSSRPC for miscellaneous errors */ 295 static void 296 log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char *error, char *data) 297 { 298 krb5_klog_syslog(LOG_NOTICE, _("Miscellaneous RPC error: %s, %s"), 299 client_addr(rqst->rq_xprt), error); 300 } 301 302 static void 303 log_badauth_display_status_1(char *m, OM_uint32 code, int type) 304 { 305 OM_uint32 gssstat, minor_stat; 306 gss_buffer_desc msg; 307 OM_uint32 msg_ctx; 308 309 msg_ctx = 0; 310 while (1) { 311 gssstat = gss_display_status(&minor_stat, code, type, GSS_C_NULL_OID, 312 &msg_ctx, &msg); 313 if (gssstat != GSS_S_COMPLETE) { 314 krb5_klog_syslog(LOG_ERR, _("%s Cannot decode status %d"), m, 315 (int)code); 316 return; 317 } 318 319 krb5_klog_syslog(LOG_NOTICE, "%s %.*s", m, (int)msg.length, 320 (char *)msg.value); 321 (void)gss_release_buffer(&minor_stat, &msg); 322 323 if (!msg_ctx) 324 break; 325 } 326 } 327 328 /* Callback from GSSRPC for authentication failures */ 329 void 330 log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data) 331 { 332 krb5_klog_syslog(LOG_NOTICE, _("Authentication attempt failed: %s, " 333 "GSS-API error strings are:"), 334 client_addr(xprt)); 335 log_badauth_display_status_1(" ", major, GSS_C_GSS_CODE); 336 log_badauth_display_status_1(" ", minor, GSS_C_MECH_CODE); 337 krb5_klog_syslog(LOG_NOTICE, _(" GSS-API error strings complete.")); 338 } 339 340 int 341 main(int argc, char *argv[]) 342 { 343 OM_uint32 minor_status; 344 gss_buffer_desc in_buf; 345 gss_OID nt_krb5_name_oid = (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME; 346 auth_gssapi_name names[4]; 347 kadm5_config_params params; 348 verto_ctx *vctx; 349 const char *pid_file = NULL; 350 char **db_args = NULL, **tmpargs; 351 const char *acl_file; 352 int ret, i, db_args_size = 0, proponly = 0; 353 354 setlocale(LC_ALL, ""); 355 setvbuf(stderr, NULL, _IONBF, 0); 356 357 names[0].name = names[1].name = names[2].name = names[3].name = NULL; 358 names[0].type = names[1].type = names[2].type = names[3].type = 359 nt_krb5_name_oid; 360 361 progname = (strrchr(argv[0], '/') != NULL) ? strrchr(argv[0], '/') + 1 : 362 argv[0]; 363 364 memset(¶ms, 0, sizeof(params)); 365 366 argc--, argv++; 367 while (argc) { 368 if (strcmp(*argv, "-x") == 0) { 369 argc--, argv++; 370 if (!argc) 371 usage(); 372 db_args_size++; 373 tmpargs = realloc(db_args, sizeof(char *) * (db_args_size + 1)); 374 if (tmpargs == NULL) { 375 fprintf(stderr, _("%s: cannot initialize. Not enough " 376 "memory\n"), progname); 377 exit(1); 378 } 379 db_args = tmpargs; 380 db_args[db_args_size - 1] = *argv; 381 db_args[db_args_size] = NULL; 382 } else if (strcmp(*argv, "-r") == 0) { 383 argc--, argv++; 384 if (!argc) 385 usage(); 386 params.realm = *argv; 387 params.mask |= KADM5_CONFIG_REALM; 388 argc--, argv++; 389 continue; 390 } else if (strcmp(*argv, "-m") == 0) { 391 params.mkey_from_kbd = 1; 392 params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 393 } else if (strcmp(*argv, "-nofork") == 0) { 394 nofork = 1; 395 #ifndef DISABLE_IPROP 396 } else if (strcmp(*argv, "-proponly") == 0) { 397 proponly = 1; 398 #endif 399 } else if (strcmp(*argv, "-port") == 0) { 400 argc--, argv++; 401 if (!argc) 402 usage(); 403 params.kadmind_port = atoi(*argv); 404 params.mask |= KADM5_CONFIG_KADMIND_PORT; 405 } else if (strcmp(*argv, "-P") == 0) { 406 argc--, argv++; 407 if (!argc) 408 usage(); 409 pid_file = *argv; 410 } else if (strcmp(*argv, "-W") == 0) { 411 /* Ignore (deprecated weak random option). */ 412 } else if (strcmp(*argv, "-p") == 0) { 413 argc--, argv++; 414 if (!argc) 415 usage(); 416 kdb5_util = *argv; 417 } else if (strcmp(*argv, "-F") == 0) { 418 argc--, argv++; 419 if (!argc) 420 usage(); 421 dump_file = *argv; 422 } else if (strcmp(*argv, "-K") == 0) { 423 argc--, argv++; 424 if (!argc) 425 usage(); 426 kprop = *argv; 427 } else if (strcmp(*argv, "-k") == 0) { 428 argc--, argv++; 429 if (!argc) 430 usage(); 431 kprop_port = *argv; 432 } else { 433 break; 434 } 435 argc--, argv++; 436 } 437 438 if (argc != 0) 439 usage(); 440 441 ret = kadm5_init_krb5_context(&context); 442 if (ret) { 443 fprintf(stderr, _("%s: %s while initializing context, aborting\n"), 444 progname, error_message(ret)); 445 exit(1); 446 } 447 448 krb5_klog_init(context, "admin_server", progname, 1); 449 450 ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms, 451 KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args, 452 &global_server_handle); 453 if (ret) 454 fail_to_start(ret, _("initializing")); 455 456 ret = kadm5_get_config_params(context, 1, ¶ms, ¶ms); 457 if (ret) 458 fail_to_start(ret, _("getting config parameters")); 459 if (!(params.mask & KADM5_CONFIG_REALM)) 460 fail_to_start(0, _("Missing required realm configuration")); 461 if (!(params.mask & KADM5_CONFIG_ACL_FILE)) 462 fail_to_start(0, _("Missing required ACL file configuration")); 463 if (proponly && !params.iprop_enabled) { 464 fail_to_start(0, _("-proponly can only be used when " 465 "iprop_enable is true")); 466 } 467 468 ret = setup_loop(¶ms, proponly, &vctx); 469 if (ret) 470 fail_to_start(ret, _("initializing network")); 471 472 names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm); 473 names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm); 474 if (names[0].name == NULL || names[1].name == NULL) 475 fail_to_start(0, _("Cannot build GSSAPI auth names")); 476 477 ret = setup_kdb_keytab(); 478 if (ret) 479 fail_to_start(0, _("Cannot set up KDB keytab")); 480 481 if (svcauth_gssapi_set_names(names, 2) == FALSE) 482 fail_to_start(0, _("Cannot set GSSAPI authentication names")); 483 484 /* if set_names succeeded, this will too */ 485 in_buf.value = names[1].name; 486 in_buf.length = strlen(names[1].name) + 1; 487 (void)gss_import_name(&minor_status, &in_buf, nt_krb5_name_oid, 488 &gss_changepw_name); 489 490 svcauth_gssapi_set_log_badauth2_func(log_badauth, NULL); 491 svcauth_gssapi_set_log_badverf_func(log_badverf, NULL); 492 svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL); 493 494 svcauth_gss_set_log_badauth2_func(log_badauth, NULL); 495 svcauth_gss_set_log_badverf_func(log_badverf, NULL); 496 svcauth_gss_set_log_miscerr_func(log_miscerr, NULL); 497 498 if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE) 499 fail_to_start(0, _("Cannot initialize GSSAPI service name")); 500 501 acl_file = (*params.acl_file != '\0') ? params.acl_file : NULL; 502 ret = auth_init(context, acl_file); 503 if (ret) 504 fail_to_start(ret, _("initializing ACL file")); 505 506 /* Since some KDB modules are not fork-safe, we must reinitialize the 507 * server handle after daemonizing. */ 508 kadm5_destroy(global_server_handle); 509 global_server_handle = NULL; 510 511 if (!nofork && daemon(0, 0) != 0) 512 fail_to_start(errno, _("spawning daemon process")); 513 if (pid_file != NULL) { 514 ret = write_pid_file(pid_file); 515 if (ret) 516 fail_to_start(ret, _("creating PID file")); 517 } 518 519 ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms, 520 KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args, 521 &global_server_handle); 522 if (ret) 523 fail_to_start(ret, _("initializing")); 524 525 if (params.iprop_enabled == TRUE) { 526 ulog_set_role(context, IPROP_PRIMARY); 527 528 ret = ulog_map(context, params.iprop_logfile, params.iprop_ulogsize); 529 if (ret) 530 fail_to_start(ret, _("mapping update log")); 531 532 if (nofork) { 533 fprintf(stderr, 534 _("%s: create IPROP svc (PROG=%d, VERS=%d)\n"), 535 progname, KRB5_IPROP_PROG, KRB5_IPROP_VERS); 536 } 537 } 538 539 if (kprop_port == NULL) 540 kprop_port = getenv("KPROP_PORT"); 541 542 krb5_klog_syslog(LOG_INFO, _("starting")); 543 if (nofork) 544 fprintf(stderr, _("%s: starting...\n"), progname); 545 546 verto_run(vctx); 547 krb5_klog_syslog(LOG_INFO, _("finished, exiting")); 548 549 /* Clean up memory, etc */ 550 svcauth_gssapi_unset_names(); 551 kadm5_destroy(global_server_handle); 552 loop_free(vctx); 553 auth_fini(context); 554 (void)gss_release_name(&minor_status, &gss_changepw_name); 555 (void)gss_release_name(&minor_status, &gss_oldchangepw_name); 556 for (i = 0; i < 4; i++) 557 free(names[i].name); 558 559 krb5_klog_close(context); 560 krb5_free_context(context); 561 exit(0); 562 } 563