/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * All rights reserved. * * Export of this software from the United States of America may require * a specific license from the United States Government. It is the * responsibility of any person or organization contemplating export to * obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #ifdef _AIX #include #endif #include #include #include #include #include #include #include #include "gssapiP_krb5.h" /* for kg_get_context */ #include #include #include #include #include "kdb_kt.h" /* for krb5_ktkdb_set_context */ #include #include #include "misc.h" #include "auth.h" #if defined(NEED_DAEMON_PROTO) int daemon(int, int); #endif #define TIMEOUT 15 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL; void *global_server_handle; int nofork = 0; char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL; char *kprop = KPROPD_DEFAULT_KPROP; char *dump_file = KPROP_DEFAULT_FILE; char *kprop_port = NULL; static krb5_context context; static char *progname; static void usage() { fprintf(stderr, _("Usage: kadmind [-x db_args]* [-r realm] [-m] [-nofork] " "[-port port-number]\n" "\t\t[-proponly] [-p path-to-kdb5_util] [-F dump-file]\n" "\t\t[-K path-to-kprop] [-k kprop-port] [-P pid_file]\n" "\nwhere,\n\t[-x db_args]* - any number of database " "specific arguments.\n" "\t\t\tLook at each database documentation for " "supported arguments\n")); exit(1); } /* * Output a message to stderr and the admin server log, and exit with status 1. * msg should not be punctuated. If code is given, msg should indicate what * operation was taking place in the present progressive. Otherwise msg should * be capitalized and should indicate what went wrong. */ static void fail_to_start(krb5_error_code code, const char *msg) { const char *errmsg; if (code) { errmsg = krb5_get_error_message(context, code); fprintf(stderr, _("%s: %s while %s, aborting\n"), progname, errmsg, msg); krb5_klog_syslog(LOG_ERR, _("%s while %s, aborting\n"), errmsg, msg); } else { fprintf(stderr, _("%s: %s, aborting\n"), progname, msg); krb5_klog_syslog(LOG_ERR, _("%s, aborting"), msg); } exit(1); } static int write_pid_file(const char *pid_file) { FILE *file; unsigned long pid; int st1, st2; file = fopen(pid_file, "w"); if (file == NULL) return errno; pid = (unsigned long)getpid(); st1 = (fprintf(file, "%ld\n", pid) < 0) ? errno : 0; st2 = (fclose(file) == EOF) ? errno : 0; return st1 ? st1 : st2; } /* Set up the main loop. If proponly is set, don't set up ports for kpasswd or * kadmin. May set *ctx_out even on error. */ static krb5_error_code setup_loop(kadm5_config_params *params, int proponly, verto_ctx **ctx_out) { krb5_error_code ret; verto_ctx *ctx; *ctx_out = ctx = loop_init(VERTO_EV_TYPE_SIGNAL); if (ctx == NULL) return ENOMEM; ret = loop_setup_signals(ctx, &global_server_handle, NULL); if (ret) return ret; if (!proponly) { ret = loop_add_udp_address(params->kpasswd_port, params->kpasswd_listen); if (ret) return ret; ret = loop_add_tcp_address(params->kpasswd_port, params->kpasswd_listen); if (ret) return ret; ret = loop_add_rpc_service(params->kadmind_port, params->kadmind_listen, KADM, KADMVERS, kadm_1); if (ret) return ret; } #ifndef DISABLE_IPROP if (params->iprop_enabled) { ret = loop_add_rpc_service(params->iprop_port, params->iprop_listen, KRB5_IPROP_PROG, KRB5_IPROP_VERS, krb5_iprop_prog_1); if (ret) return ret; } #endif return loop_setup_network(ctx, &global_server_handle, progname, DEFAULT_TCP_LISTEN_BACKLOG); } /* Point GSSAPI at the KDB keytab so we don't need an actual file keytab. */ static krb5_error_code setup_kdb_keytab() { krb5_error_code ret; ret = krb5_ktkdb_set_context(context); if (ret) return ret; ret = krb5_db_register_keytab(context); if (ret) return ret; return krb5_gss_register_acceptor_identity("KDB:"); } /* Return "name@realm". */ static char * build_princ_name(char *name, char *realm) { char *fullname; if (asprintf(&fullname, "%s@%s", name, realm) < 0) return NULL; return fullname; } /* Callback from GSSRPC for garbled/forged/replayed/etc messages. */ static void log_badverf(gss_name_t client_name, gss_name_t server_name, struct svc_req *rqst, struct rpc_msg *msg, char *data) { static const struct { rpcproc_t proc; const char *proc_name; } proc_names[] = { {1, "CREATE_PRINCIPAL"}, {2, "DELETE_PRINCIPAL"}, {3, "MODIFY_PRINCIPAL"}, {4, "RENAME_PRINCIPAL"}, {5, "GET_PRINCIPAL"}, {6, "CHPASS_PRINCIPAL"}, {7, "CHRAND_PRINCIPAL"}, {8, "CREATE_POLICY"}, {9, "DELETE_POLICY"}, {10, "MODIFY_POLICY"}, {11, "GET_POLICY"}, {12, "GET_PRIVS"}, {13, "INIT"}, {14, "GET_PRINCS"}, {15, "GET_POLS"}, {16, "SETKEY_PRINCIPAL"}, /* 17 was "SETV4KEY_PRINCIPAL" */ {18, "CREATE_PRINCIPAL3"}, {19, "CHPASS_PRINCIPAL3"}, {20, "CHRAND_PRINCIPAL3"}, {21, "SETKEY_PRINCIPAL3"}, {22, "PURGEKEYS"}, {23, "GET_STRINGS"}, {24, "SET_STRING"} }; OM_uint32 minor; gss_buffer_desc client, server; gss_OID gss_type; const char *a; rpcproc_t proc; unsigned int i; const char *procname; size_t clen, slen; char *cdots, *sdots; client.length = 0; client.value = NULL; server.length = 0; server.value = NULL; (void)gss_display_name(&minor, client_name, &client, &gss_type); (void)gss_display_name(&minor, server_name, &server, &gss_type); if (client.value == NULL) { client.value = "(null)"; clen = sizeof("(null)") - 1; } else { clen = client.length; } trunc_name(&clen, &cdots); if (server.value == NULL) { server.value = "(null)"; slen = sizeof("(null)") - 1; } else { slen = server.length; } trunc_name(&slen, &sdots); a = client_addr(rqst->rq_xprt); proc = msg->rm_call.cb_proc; procname = NULL; for (i = 0; i < sizeof(proc_names) / sizeof(*proc_names); i++) { if (proc_names[i].proc == proc) { procname = proc_names[i].proc_name; break; } } if (procname != NULL) { krb5_klog_syslog(LOG_NOTICE, _("WARNING! Forged/garbled request: %s, claimed " "client = %.*s%s, server = %.*s%s, addr = %s"), procname, (int)clen, (char *)client.value, cdots, (int)slen, (char *)server.value, sdots, a); } else { krb5_klog_syslog(LOG_NOTICE, _("WARNING! Forged/garbled request: %d, claimed " "client = %.*s%s, server = %.*s%s, addr = %s"), proc, (int)clen, (char *)client.value, cdots, (int)slen, (char *)server.value, sdots, a); } (void)gss_release_buffer(&minor, &client); (void)gss_release_buffer(&minor, &server); } /* Callback from GSSRPC for miscellaneous errors */ static void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char *error, char *data) { krb5_klog_syslog(LOG_NOTICE, _("Miscellaneous RPC error: %s, %s"), client_addr(rqst->rq_xprt), error); } static void log_badauth_display_status_1(char *m, OM_uint32 code, int type) { OM_uint32 gssstat, minor_stat; gss_buffer_desc msg; OM_uint32 msg_ctx; msg_ctx = 0; while (1) { gssstat = gss_display_status(&minor_stat, code, type, GSS_C_NULL_OID, &msg_ctx, &msg); if (gssstat != GSS_S_COMPLETE) { krb5_klog_syslog(LOG_ERR, _("%s Cannot decode status %d"), m, (int)code); return; } krb5_klog_syslog(LOG_NOTICE, "%s %.*s", m, (int)msg.length, (char *)msg.value); (void)gss_release_buffer(&minor_stat, &msg); if (!msg_ctx) break; } } /* Callback from GSSRPC for authentication failures */ void log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data) { krb5_klog_syslog(LOG_NOTICE, _("Authentication attempt failed: %s, " "GSS-API error strings are:"), client_addr(xprt)); log_badauth_display_status_1(" ", major, GSS_C_GSS_CODE); log_badauth_display_status_1(" ", minor, GSS_C_MECH_CODE); krb5_klog_syslog(LOG_NOTICE, _(" GSS-API error strings complete.")); } int main(int argc, char *argv[]) { OM_uint32 minor_status; gss_buffer_desc in_buf; gss_OID nt_krb5_name_oid = (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME; auth_gssapi_name names[4]; kadm5_config_params params; verto_ctx *vctx; const char *pid_file = NULL; char **db_args = NULL, **tmpargs; const char *acl_file; int ret, i, db_args_size = 0, strong_random = 1, proponly = 0; setlocale(LC_ALL, ""); setvbuf(stderr, NULL, _IONBF, 0); names[0].name = names[1].name = names[2].name = names[3].name = NULL; names[0].type = names[1].type = names[2].type = names[3].type = nt_krb5_name_oid; progname = (strrchr(argv[0], '/') != NULL) ? strrchr(argv[0], '/') + 1 : argv[0]; memset(¶ms, 0, sizeof(params)); argc--, argv++; while (argc) { if (strcmp(*argv, "-x") == 0) { argc--, argv++; if (!argc) usage(); db_args_size++; tmpargs = realloc(db_args, sizeof(char *) * (db_args_size + 1)); if (tmpargs == NULL) { fprintf(stderr, _("%s: cannot initialize. Not enough " "memory\n"), progname); exit(1); } db_args = tmpargs; db_args[db_args_size - 1] = *argv; db_args[db_args_size] = NULL; } else if (strcmp(*argv, "-r") == 0) { argc--, argv++; if (!argc) usage(); params.realm = *argv; params.mask |= KADM5_CONFIG_REALM; argc--, argv++; continue; } else if (strcmp(*argv, "-m") == 0) { params.mkey_from_kbd = 1; params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; } else if (strcmp(*argv, "-nofork") == 0) { nofork = 1; #ifndef DISABLE_IPROP } else if (strcmp(*argv, "-proponly") == 0) { proponly = 1; #endif } else if (strcmp(*argv, "-port") == 0) { argc--, argv++; if (!argc) usage(); params.kadmind_port = atoi(*argv); params.mask |= KADM5_CONFIG_KADMIND_PORT; } else if (strcmp(*argv, "-P") == 0) { argc--, argv++; if (!argc) usage(); pid_file = *argv; } else if (strcmp(*argv, "-W") == 0) { strong_random = 0; } else if (strcmp(*argv, "-p") == 0) { argc--, argv++; if (!argc) usage(); kdb5_util = *argv; } else if (strcmp(*argv, "-F") == 0) { argc--, argv++; if (!argc) usage(); dump_file = *argv; } else if (strcmp(*argv, "-K") == 0) { argc--, argv++; if (!argc) usage(); kprop = *argv; } else if (strcmp(*argv, "-k") == 0) { argc--, argv++; if (!argc) usage(); kprop_port = *argv; } else { break; } argc--, argv++; } if (argc != 0) usage(); ret = kadm5_init_krb5_context(&context); if (ret) { fprintf(stderr, _("%s: %s while initializing context, aborting\n"), progname, error_message(ret)); exit(1); } krb5_klog_init(context, "admin_server", progname, 1); ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args, &global_server_handle); if (ret) fail_to_start(ret, _("initializing")); ret = kadm5_get_config_params(context, 1, ¶ms, ¶ms); if (ret) fail_to_start(ret, _("getting config parameters")); if (!(params.mask & KADM5_CONFIG_REALM)) fail_to_start(0, _("Missing required realm configuration")); if (!(params.mask & KADM5_CONFIG_ACL_FILE)) fail_to_start(0, _("Missing required ACL file configuration")); if (proponly && !params.iprop_enabled) { fail_to_start(0, _("-proponly can only be used when " "iprop_enable is true")); } ret = setup_loop(¶ms, proponly, &vctx); if (ret) fail_to_start(ret, _("initializing network")); names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm); names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm); if (names[0].name == NULL || names[1].name == NULL) fail_to_start(0, _("Cannot build GSSAPI auth names")); ret = setup_kdb_keytab(); if (ret) fail_to_start(0, _("Cannot set up KDB keytab")); if (svcauth_gssapi_set_names(names, 2) == FALSE) fail_to_start(0, _("Cannot set GSSAPI authentication names")); /* if set_names succeeded, this will too */ in_buf.value = names[1].name; in_buf.length = strlen(names[1].name) + 1; (void)gss_import_name(&minor_status, &in_buf, nt_krb5_name_oid, &gss_changepw_name); svcauth_gssapi_set_log_badauth2_func(log_badauth, NULL); svcauth_gssapi_set_log_badverf_func(log_badverf, NULL); svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL); svcauth_gss_set_log_badauth2_func(log_badauth, NULL); svcauth_gss_set_log_badverf_func(log_badverf, NULL); svcauth_gss_set_log_miscerr_func(log_miscerr, NULL); if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE) fail_to_start(0, _("Cannot initialize GSSAPI service name")); acl_file = (*params.acl_file != '\0') ? params.acl_file : NULL; ret = auth_init(context, acl_file); if (ret) fail_to_start(ret, _("initializing ACL file")); /* Since some KDB modules are not fork-safe, we must reinitialize the * server handle after daemonizing. */ kadm5_destroy(global_server_handle); global_server_handle = NULL; if (!nofork && daemon(0, 0) != 0) fail_to_start(errno, _("spawning daemon process")); if (pid_file != NULL) { ret = write_pid_file(pid_file); if (ret) fail_to_start(ret, _("creating PID file")); } ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args, &global_server_handle); if (ret) fail_to_start(ret, _("initializing")); krb5_klog_syslog(LOG_INFO, _("Seeding random number generator")); ret = krb5_c_random_os_entropy(context, strong_random, NULL); if (ret) fail_to_start(ret, _("getting random seed")); if (params.iprop_enabled == TRUE) { ulog_set_role(context, IPROP_PRIMARY); ret = ulog_map(context, params.iprop_logfile, params.iprop_ulogsize); if (ret) fail_to_start(ret, _("mapping update log")); if (nofork) { fprintf(stderr, _("%s: create IPROP svc (PROG=%d, VERS=%d)\n"), progname, KRB5_IPROP_PROG, KRB5_IPROP_VERS); } } if (kprop_port == NULL) kprop_port = getenv("KPROP_PORT"); krb5_klog_syslog(LOG_INFO, _("starting")); if (nofork) fprintf(stderr, _("%s: starting...\n"), progname); verto_run(vctx); krb5_klog_syslog(LOG_INFO, _("finished, exiting")); /* Clean up memory, etc */ svcauth_gssapi_unset_names(); kadm5_destroy(global_server_handle); loop_free(vctx); auth_fini(context); (void)gss_release_name(&minor_status, &gss_changepw_name); (void)gss_release_name(&minor_status, &gss_oldchangepw_name); for (i = 0; i < 4; i++) free(names[i].name); krb5_klog_close(context); krb5_free_context(context); exit(0); }