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(void) 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 ret = loop_add_unix_socket(params->kpasswd_listen); 161 if (ret) 162 return ret; 163 } 164 #ifndef DISABLE_IPROP 165 if (params->iprop_enabled) { 166 ret = loop_add_rpc_service(params->iprop_port, params->iprop_listen, 167 KRB5_IPROP_PROG, KRB5_IPROP_VERS, 168 krb5_iprop_prog_1); 169 if (ret) 170 return ret; 171 } 172 #endif 173 return loop_setup_network(ctx, &global_server_handle, progname, 174 DEFAULT_TCP_LISTEN_BACKLOG); 175 } 176 177 /* Point GSSAPI at the KDB keytab so we don't need an actual file keytab. */ 178 static krb5_error_code 179 setup_kdb_keytab(void) 180 { 181 krb5_error_code ret; 182 183 ret = krb5_ktkdb_set_context(context); 184 if (ret) 185 return ret; 186 ret = krb5_db_register_keytab(context); 187 if (ret) 188 return ret; 189 return krb5_gss_register_acceptor_identity("KDB:"); 190 } 191 192 193 /* Return "name@realm". */ 194 static char * 195 build_princ_name(char *name, char *realm) 196 { 197 char *fullname; 198 199 if (asprintf(&fullname, "%s@%s", name, realm) < 0) 200 return NULL; 201 return fullname; 202 } 203 204 /* Callback from GSSRPC for garbled/forged/replayed/etc messages. */ 205 static void 206 log_badverf(gss_name_t client_name, gss_name_t server_name, 207 struct svc_req *rqst, struct rpc_msg *msg, char *data) 208 { 209 static const struct { 210 rpcproc_t proc; 211 const char *proc_name; 212 } proc_names[] = { 213 {1, "CREATE_PRINCIPAL"}, 214 {2, "DELETE_PRINCIPAL"}, 215 {3, "MODIFY_PRINCIPAL"}, 216 {4, "RENAME_PRINCIPAL"}, 217 {5, "GET_PRINCIPAL"}, 218 {6, "CHPASS_PRINCIPAL"}, 219 {7, "CHRAND_PRINCIPAL"}, 220 {8, "CREATE_POLICY"}, 221 {9, "DELETE_POLICY"}, 222 {10, "MODIFY_POLICY"}, 223 {11, "GET_POLICY"}, 224 {12, "GET_PRIVS"}, 225 {13, "INIT"}, 226 {14, "GET_PRINCS"}, 227 {15, "GET_POLS"}, 228 {16, "SETKEY_PRINCIPAL"}, 229 /* 17 was "SETV4KEY_PRINCIPAL" */ 230 {18, "CREATE_PRINCIPAL3"}, 231 {19, "CHPASS_PRINCIPAL3"}, 232 {20, "CHRAND_PRINCIPAL3"}, 233 {21, "SETKEY_PRINCIPAL3"}, 234 {22, "PURGEKEYS"}, 235 {23, "GET_STRINGS"}, 236 {24, "SET_STRING"} 237 }; 238 OM_uint32 minor; 239 gss_buffer_desc client, server; 240 gss_OID gss_type; 241 const char *a, *cname, *sname; 242 rpcproc_t proc; 243 unsigned int i; 244 const char *procname; 245 size_t clen, slen; 246 char *cdots, *sdots; 247 248 client.length = 0; 249 client.value = NULL; 250 server.length = 0; 251 server.value = NULL; 252 253 (void)gss_display_name(&minor, client_name, &client, &gss_type); 254 (void)gss_display_name(&minor, server_name, &server, &gss_type); 255 cname = (client.value == NULL) ? "(null)" : client.value; 256 clen = (client.value == NULL) ? sizeof("(null)") - 1 : client.length; 257 trunc_name(&clen, &cdots); 258 sname = (server.value == NULL) ? "(null)" : server.value; 259 slen = (server.value == NULL) ? sizeof("(null)") - 1 : server.length; 260 trunc_name(&slen, &sdots); 261 a = client_addr(rqst->rq_xprt); 262 263 proc = msg->rm_call.cb_proc; 264 procname = NULL; 265 for (i = 0; i < sizeof(proc_names) / sizeof(*proc_names); i++) { 266 if (proc_names[i].proc == proc) { 267 procname = proc_names[i].proc_name; 268 break; 269 } 270 } 271 if (procname != NULL) { 272 krb5_klog_syslog(LOG_NOTICE, 273 _("WARNING! Forged/garbled request: %s, claimed " 274 "client = %.*s%s, server = %.*s%s, addr = %s"), 275 procname, (int)clen, cname, cdots, (int)slen, sname, 276 sdots, a); 277 } else { 278 krb5_klog_syslog(LOG_NOTICE, 279 _("WARNING! Forged/garbled request: %d, claimed " 280 "client = %.*s%s, server = %.*s%s, addr = %s"), 281 proc, (int)clen, cname, cdots, (int)slen, sname, 282 sdots, a); 283 } 284 285 (void)gss_release_buffer(&minor, &client); 286 (void)gss_release_buffer(&minor, &server); 287 } 288 289 /* Callback from GSSRPC for miscellaneous errors */ 290 static void 291 log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char *error, char *data) 292 { 293 krb5_klog_syslog(LOG_NOTICE, _("Miscellaneous RPC error: %s, %s"), 294 client_addr(rqst->rq_xprt), error); 295 } 296 297 static void 298 log_badauth_display_status_1(char *m, OM_uint32 code, int type) 299 { 300 OM_uint32 gssstat, minor_stat; 301 gss_buffer_desc msg; 302 OM_uint32 msg_ctx; 303 304 msg_ctx = 0; 305 while (1) { 306 gssstat = gss_display_status(&minor_stat, code, type, GSS_C_NULL_OID, 307 &msg_ctx, &msg); 308 if (gssstat != GSS_S_COMPLETE) { 309 krb5_klog_syslog(LOG_ERR, _("%s Cannot decode status %d"), m, 310 (int)code); 311 return; 312 } 313 314 krb5_klog_syslog(LOG_NOTICE, "%s %.*s", m, (int)msg.length, 315 (char *)msg.value); 316 (void)gss_release_buffer(&minor_stat, &msg); 317 318 if (!msg_ctx) 319 break; 320 } 321 } 322 323 /* Callback from GSSRPC for authentication failures */ 324 void 325 log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data) 326 { 327 krb5_klog_syslog(LOG_NOTICE, _("Authentication attempt failed: %s, " 328 "GSS-API error strings are:"), 329 client_addr(xprt)); 330 log_badauth_display_status_1(" ", major, GSS_C_GSS_CODE); 331 log_badauth_display_status_1(" ", minor, GSS_C_MECH_CODE); 332 krb5_klog_syslog(LOG_NOTICE, _(" GSS-API error strings complete.")); 333 } 334 335 int 336 main(int argc, char *argv[]) 337 { 338 OM_uint32 minor_status; 339 gss_buffer_desc in_buf; 340 gss_OID nt_krb5_name_oid = (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME; 341 auth_gssapi_name names[4]; 342 kadm5_config_params params; 343 verto_ctx *vctx; 344 const char *pid_file = NULL; 345 char **db_args = NULL, **tmpargs; 346 const char *acl_file; 347 size_t db_args_size = 0; 348 int ret, i, proponly = 0; 349 350 setlocale(LC_ALL, ""); 351 setvbuf(stderr, NULL, _IONBF, 0); 352 353 names[0].name = names[1].name = names[2].name = names[3].name = NULL; 354 names[0].type = names[1].type = names[2].type = names[3].type = 355 nt_krb5_name_oid; 356 357 progname = (strrchr(argv[0], '/') != NULL) ? strrchr(argv[0], '/') + 1 : 358 argv[0]; 359 360 memset(¶ms, 0, sizeof(params)); 361 362 argc--, argv++; 363 while (argc) { 364 if (strcmp(*argv, "-x") == 0) { 365 argc--, argv++; 366 if (!argc) 367 usage(); 368 db_args_size++; 369 tmpargs = realloc(db_args, sizeof(char *) * (db_args_size + 1)); 370 if (tmpargs == NULL) { 371 fprintf(stderr, _("%s: cannot initialize. Not enough " 372 "memory\n"), progname); 373 exit(1); 374 } 375 db_args = tmpargs; 376 db_args[db_args_size - 1] = *argv; 377 db_args[db_args_size] = NULL; 378 } else if (strcmp(*argv, "-r") == 0) { 379 argc--, argv++; 380 if (!argc) 381 usage(); 382 params.realm = *argv; 383 params.mask |= KADM5_CONFIG_REALM; 384 argc--, argv++; 385 continue; 386 } else if (strcmp(*argv, "-m") == 0) { 387 params.mkey_from_kbd = 1; 388 params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 389 } else if (strcmp(*argv, "-nofork") == 0) { 390 nofork = 1; 391 #ifndef DISABLE_IPROP 392 } else if (strcmp(*argv, "-proponly") == 0) { 393 proponly = 1; 394 #endif 395 } else if (strcmp(*argv, "-port") == 0) { 396 argc--, argv++; 397 if (!argc) 398 usage(); 399 params.kadmind_port = atoi(*argv); 400 params.mask |= KADM5_CONFIG_KADMIND_PORT; 401 } else if (strcmp(*argv, "-P") == 0) { 402 argc--, argv++; 403 if (!argc) 404 usage(); 405 pid_file = *argv; 406 } else if (strcmp(*argv, "-W") == 0) { 407 /* Ignore (deprecated weak random option). */ 408 } else if (strcmp(*argv, "-p") == 0) { 409 argc--, argv++; 410 if (!argc) 411 usage(); 412 kdb5_util = *argv; 413 } else if (strcmp(*argv, "-F") == 0) { 414 argc--, argv++; 415 if (!argc) 416 usage(); 417 dump_file = *argv; 418 } else if (strcmp(*argv, "-K") == 0) { 419 argc--, argv++; 420 if (!argc) 421 usage(); 422 kprop = *argv; 423 } else if (strcmp(*argv, "-k") == 0) { 424 argc--, argv++; 425 if (!argc) 426 usage(); 427 kprop_port = *argv; 428 } else { 429 break; 430 } 431 argc--, argv++; 432 } 433 434 if (argc != 0) 435 usage(); 436 437 ret = kadm5_init_krb5_context(&context); 438 if (ret) { 439 fprintf(stderr, _("%s: %s while initializing context, aborting\n"), 440 progname, error_message(ret)); 441 exit(1); 442 } 443 444 krb5_klog_init(context, "admin_server", progname, 1); 445 446 ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms, 447 KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args, 448 &global_server_handle); 449 if (ret) 450 fail_to_start(ret, _("initializing")); 451 452 ret = kadm5_get_config_params(context, 1, ¶ms, ¶ms); 453 if (ret) 454 fail_to_start(ret, _("getting config parameters")); 455 if (!(params.mask & KADM5_CONFIG_REALM)) 456 fail_to_start(0, _("Missing required realm configuration")); 457 if (!(params.mask & KADM5_CONFIG_ACL_FILE)) 458 fail_to_start(0, _("Missing required ACL file configuration")); 459 if (proponly && !params.iprop_enabled) { 460 fail_to_start(0, _("-proponly can only be used when " 461 "iprop_enable is true")); 462 } 463 464 ret = setup_loop(¶ms, proponly, &vctx); 465 if (ret) 466 fail_to_start(ret, _("initializing network")); 467 468 names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm); 469 names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm); 470 if (names[0].name == NULL || names[1].name == NULL) 471 fail_to_start(0, _("Cannot build GSSAPI auth names")); 472 473 ret = setup_kdb_keytab(); 474 if (ret) 475 fail_to_start(0, _("Cannot set up KDB keytab")); 476 477 if (svcauth_gssapi_set_names(names, 2) == FALSE) 478 fail_to_start(0, _("Cannot set GSSAPI authentication names")); 479 480 /* if set_names succeeded, this will too */ 481 in_buf.value = names[1].name; 482 in_buf.length = strlen(names[1].name) + 1; 483 (void)gss_import_name(&minor_status, &in_buf, nt_krb5_name_oid, 484 &gss_changepw_name); 485 486 svcauth_gssapi_set_log_badauth2_func(log_badauth, NULL); 487 svcauth_gssapi_set_log_badverf_func(log_badverf, NULL); 488 svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL); 489 490 svcauth_gss_set_log_badauth2_func(log_badauth, NULL); 491 svcauth_gss_set_log_badverf_func(log_badverf, NULL); 492 svcauth_gss_set_log_miscerr_func(log_miscerr, NULL); 493 494 if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE) 495 fail_to_start(0, _("Cannot initialize GSSAPI service name")); 496 497 acl_file = (*params.acl_file != '\0') ? params.acl_file : NULL; 498 ret = auth_init(context, acl_file); 499 if (ret) 500 fail_to_start(ret, _("initializing ACL file")); 501 502 /* Since some KDB modules are not fork-safe, we must reinitialize the 503 * server handle after daemonizing. */ 504 kadm5_destroy(global_server_handle); 505 global_server_handle = NULL; 506 507 if (!nofork && daemon(0, 0) != 0) 508 fail_to_start(errno, _("spawning daemon process")); 509 if (pid_file != NULL) { 510 ret = write_pid_file(pid_file); 511 if (ret) 512 fail_to_start(ret, _("creating PID file")); 513 } 514 515 ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms, 516 KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args, 517 &global_server_handle); 518 if (ret) 519 fail_to_start(ret, _("initializing")); 520 521 if (params.iprop_enabled == TRUE) { 522 ulog_set_role(context, IPROP_PRIMARY); 523 524 ret = ulog_map(context, params.iprop_logfile, params.iprop_ulogsize); 525 if (ret) 526 fail_to_start(ret, _("mapping update log")); 527 528 if (nofork) { 529 fprintf(stderr, 530 _("%s: create IPROP svc (PROG=%d, VERS=%d)\n"), 531 progname, KRB5_IPROP_PROG, KRB5_IPROP_VERS); 532 } 533 } 534 535 if (kprop_port == NULL) 536 kprop_port = getenv("KPROP_PORT"); 537 538 krb5_klog_syslog(LOG_INFO, _("starting")); 539 if (nofork) 540 fprintf(stderr, _("%s: starting...\n"), progname); 541 542 verto_run(vctx); 543 krb5_klog_syslog(LOG_INFO, _("finished, exiting")); 544 545 /* Clean up memory, etc */ 546 svcauth_gssapi_unset_names(); 547 kadm5_destroy(global_server_handle); 548 loop_free(vctx); 549 auth_fini(context); 550 (void)gss_release_name(&minor_status, &gss_changepw_name); 551 (void)gss_release_name(&minor_status, &gss_oldchangepw_name); 552 for (i = 0; i < 4; i++) 553 free(names[i].name); 554 555 krb5_klog_close(context); 556 krb5_free_context(context); 557 exit(0); 558 } 559