#include <stdio.h>
#include <signal.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/param.h>
#ifdef _AIX
#include <sys/select.h>
#endif
#include <sys/time.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <libutil.h>
#include <gssrpc/rpc.h>
#include <gssapi/gssapi.h>
#include "gssapiP_krb5.h"
#include <gssrpc/auth_gssapi.h>
#include <kadm5/admin.h>
#include <kadm5/kadm_rpc.h>
#include <kadm5/server_acl.h>
#include <adm_proto.h>
#include "kdb_kt.h"
#include <string.h>
#include "kadm5/server_internal.h"
#include <kdb_log.h>
#include <sandbox.h>
#include "misc.h"
#ifdef PURIFY
#include "purify.h"
int signal_pure_report = 0;
int signal_pure_clear = 0;
void request_pure_report(int);
void request_pure_clear(int);
#endif
#if defined(NEED_DAEMON_PROTO)
extern int daemon(int, int);
#endif
static struct pidfh *pidfile;
volatile int signal_request_exit = 0;
volatile int signal_request_hup = 0;
void setup_signal_handlers(iprop_role iproprole);
void request_exit(int);
void request_hup(int);
void reset_db(void);
void sig_pipe(int);
void kadm_svc_run(kadm5_config_params *params);
#ifdef POSIX_SIGNALS
static struct sigaction s_action;
#endif
#define TIMEOUT 15
gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
gss_name_t gss_kadmin_name = NULL;
void *global_server_handle;
#define OVSEC_KADM_ADMIN_SERVICE "ovsec_adm/admin"
#define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw"
extern krb5_keyblock master_keyblock;
char *build_princ_name(char *name, char *realm);
void log_badauth(OM_uint32 major, OM_uint32 minor,
struct sockaddr_in *addr, char *data);
void log_badverf(gss_name_t client_name, gss_name_t server_name,
struct svc_req *rqst, struct rpc_msg *msg,
char *data);
void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char
*error, char *data);
void log_badauth_display_status(char *msg, OM_uint32 major, OM_uint32 minor);
void log_badauth_display_status_1(char *m, OM_uint32 code, int type,
int rec);
int schpw;
void do_schpw(int s, kadm5_config_params *params);
#ifndef DISABLE_IPROP
int ipropfd;
#endif
#ifdef USE_PASSWORD_SERVER
void kadm5_set_use_password_server (void);
#endif
extern void krb5_iprop_prog_1();
extern kadm5_ret_t kiprop_get_adm_host_srv_name(
krb5_context,
const char *,
char **);
static void usage()
{
fprintf(stderr, "Usage: kadmind [-x db_args]* [-r realm] [-m] [-nofork] "
#ifdef USE_PASSWORD_SERVER
"[-passwordserver] "
#endif
"[-port port-number]\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);
}
static void display_status_1(char *, OM_uint32, int);
static void display_status(msg, maj_stat, min_stat)
char *msg;
OM_uint32 maj_stat;
OM_uint32 min_stat;
{
display_status_1(msg, maj_stat, GSS_C_GSS_CODE);
display_status_1(msg, min_stat, GSS_C_MECH_CODE);
}
static void display_status_1(m, code, type)
char *m;
OM_uint32 code;
int type;
{
OM_uint32 maj_stat, min_stat;
gss_buffer_desc msg;
OM_uint32 msg_ctx;
msg_ctx = 0;
while (1) {
maj_stat = gss_display_status(&min_stat, code,
type, GSS_C_NULL_OID,
&msg_ctx, &msg);
fprintf(stderr, "GSS-API error %s: %s\n", m,
(char *)msg.value);
(void) gss_release_buffer(&min_stat, &msg);
if (!msg_ctx)
break;
}
}
static krb5_context context;
static krb5_context hctx;
int nofork = 0;
int main(int argc, char *argv[])
{
register SVCXPRT *transp, *iproptransp;
extern char *optarg;
extern int optind, opterr;
int ret, oldnames = 0;
OM_uint32 OMret, major_status, minor_status;
char *whoami;
gss_buffer_desc in_buf;
struct sockaddr_in addr;
int s;
auth_gssapi_name names[4];
gss_buffer_desc gssbuf;
gss_OID nt_krb5_name_oid;
kadm5_config_params params;
char **db_args = NULL;
int db_args_size = 0;
const char *errmsg;
char* errorbuf = NULL;
char *kiprop_name = NULL;
kdb_log_context *log_ctx;
setvbuf(stderr, NULL, _IONBF, 0);
gssbuf.value = "{1 2 840 113554 1 2 2 1}";
gssbuf.length = strlen(gssbuf.value);
major_status = gss_str_to_oid(&minor_status, &gssbuf, &nt_krb5_name_oid);
if (major_status != GSS_S_COMPLETE) {
fprintf(stderr, "Couldn't create KRB5 Name NameType OID\n");
display_status("str_to_oid", major_status, minor_status);
exit(1);
}
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;
#ifdef PURIFY
purify_start_batch();
#endif
whoami = (strrchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]);
if (0 != sandbox_init(whoami, SANDBOX_NAMED, &errorbuf)) {
fprintf(stderr, "%s: failed to initialize sandbox: %s", whoami, errorbuf);
sandbox_free_error(errorbuf);
exit(1);
}
nofork = 0;
memset((char *) ¶ms, 0, sizeof(params));
argc--; argv++;
while (argc) {
if (strcmp(*argv, "-x") == 0) {
argc--; argv++;
if (!argc)
usage();
db_args_size++;
{
char **temp = realloc( db_args, sizeof(char*) * (db_args_size+1));
if( temp == NULL )
{
fprintf(stderr,"%s: cannot initialize. Not enough memory\n",
whoami);
exit(1);
}
db_args = temp;
}
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;
#ifdef USE_PASSWORD_SERVER
} else if (strcmp(*argv, "-passwordserver") == 0) {
kadm5_set_use_password_server ();
#endif
} else if(strcmp(*argv, "-port") == 0) {
argc--; argv++;
if(!argc)
usage();
params.kadmind_port = atoi(*argv);
params.mask |= KADM5_CONFIG_KADMIND_PORT;
} else
break;
argc--; argv++;
}
if (argc != 0)
usage();
if ((ret = kadm5_init_krb5_context(&context))) {
fprintf(stderr, "%s: %s while initializing context, aborting\n",
whoami, error_message(ret));
exit(1);
}
krb5_klog_init(context, "admin_server", whoami, 1);
if((ret = kadm5_init("kadmind", NULL,
NULL, ¶ms,
KADM5_STRUCT_VERSION,
KADM5_API_VERSION_2,
db_args,
&global_server_handle)) != KADM5_OK) {
const char *e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR, "%s while initializing, aborting",
e_txt);
fprintf(stderr, "%s: %s while initializing, aborting\n",
whoami, e_txt);
krb5_klog_close(context);
exit(1);
}
if ((ret = kadm5_get_config_params(context, 1, ¶ms,
¶ms))) {
const char *e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR, "%s: %s while initializing, aborting",
whoami, e_txt);
fprintf(stderr, "%s: %s while initializing, aborting\n",
whoami, e_txt);
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE)
if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
krb5_klog_syslog(LOG_ERR, "%s: Missing required configuration values "
"(%lx) while initializing, aborting", whoami,
(params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
fprintf(stderr, "%s: Missing required configuration values "
"(%lx) while initializing, aborting\n", whoami,
(params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
krb5_klog_close(context);
kadm5_destroy(global_server_handle);
exit(1);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(params.kadmind_port);
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
const char *e_txt;
ret = SOCKET_ERRNO;
e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR, "Cannot create TCP socket: %s",
e_txt);
fprintf(stderr, "Cannot create TCP socket: %s",
e_txt);
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
set_cloexec_fd(s);
if ((schpw = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
const char *e_txt;
ret = SOCKET_ERRNO;
e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR,
"cannot create simple chpw socket: %s",
e_txt);
fprintf(stderr, "Cannot create simple chpw socket: %s",
e_txt);
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
set_cloexec_fd(schpw);
#ifdef IP_RECVDSTADDR
{
int on = 1;
if (setsockopt(schpw, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof (on)) != 0) {
krb5_klog_syslog(LOG_ERR, "Can't set IP_RECVDSTADDR on chpw socket");
fprintf(stderr, "Can't set IP_RECVDSTADDR on chpw socket\n");
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
}
#endif
#ifndef DISABLE_IPROP
if ((ipropfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
const char *e_txt;
ret = SOCKET_ERRNO;
e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR,
"cannot create iprop listening socket: %s",
e_txt);
fprintf(stderr, "cannot create iprop listening socket: %s",
e_txt);
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
set_cloexec_fd(ipropfd);
#endif
#ifdef SO_REUSEADDR
{
int allowed;
allowed = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *) &allowed, sizeof(allowed)) < 0 ||
setsockopt(schpw, SOL_SOCKET, SO_REUSEADDR,
(char *) &allowed, sizeof(allowed)) < 0
#ifndef DISABLE_IPROP
|| setsockopt(ipropfd, SOL_SOCKET, SO_REUSEADDR,
(char *) &allowed, sizeof(allowed)) < 0
#endif
) {
const char *e_txt;
ret = SOCKET_ERRNO;
e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR, "Cannot set SO_REUSEADDR: %s",
e_txt);
fprintf(stderr, "Cannot set SO_REUSEADDR: %s", e_txt);
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
}
#endif
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(params.kadmind_port);
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
int oerrno = errno;
const char *e_txt = krb5_get_error_message (context, errno);
fprintf(stderr, "%s: Cannot bind socket.\n", whoami);
fprintf(stderr, "bind: %s\n", e_txt);
errno = oerrno;
krb5_klog_syslog(LOG_ERR, "Cannot bind socket: %s", e_txt);
if(oerrno == EADDRINUSE) {
char *w = strrchr(whoami, '/');
if (w) {
w++;
}
else {
w = whoami;
}
fprintf(stderr,
"This probably means that another %s process is already\n"
"running, or that another program is using the server port (number %d)\n"
"after being assigned it by the RPC portmap daemon. If another\n"
"%s is already running, you should kill it before\n"
"restarting the server. If, on the other hand, another program is\n"
"using the server port, you should kill it before running\n"
"%s, and ensure that the conflict does not occur in the\n"
"future by making sure that %s is started on reboot\n"
"before portmap.\n", w, ntohs(addr.sin_port), w, w, w);
krb5_klog_syslog(LOG_ERR, "Check for already-running %s or for "
"another process using port %d", w,
htons(addr.sin_port));
}
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(params.kpasswd_port);
if (bind(schpw, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
char portbuf[32];
int oerrno = errno;
const char *e_txt = krb5_get_error_message (context, errno);
fprintf(stderr, "%s: Cannot bind socket.\n", whoami);
fprintf(stderr, "bind: %s\n", e_txt);
errno = oerrno;
snprintf(portbuf, sizeof(portbuf), "%d", ntohs(addr.sin_port));
krb5_klog_syslog(LOG_ERR, "cannot bind simple chpw socket: %s",
e_txt);
if(oerrno == EADDRINUSE) {
char *w = strrchr(whoami, '/');
if (w) {
w++;
}
else {
w = whoami;
}
fprintf(stderr,
"This probably means that another %s process is already\n"
"running, or that another program is using the server port (number %d).\n"
"If another %s is already running, you should kill it before\n"
"restarting the server.\n",
w, ntohs(addr.sin_port), w);
}
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
#ifndef DISABLE_IPROP
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(params.iprop_port);
if (bind(ipropfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
char portbuf[32];
int oerrno = errno;
const char *e_txt = krb5_get_error_message (context, errno);
fprintf(stderr, "%s: Cannot bind socket.\n", whoami);
fprintf(stderr, "bind: %s\n", e_txt);
errno = oerrno;
snprintf(portbuf, sizeof(portbuf), "%d", ntohs(addr.sin_port));
krb5_klog_syslog(LOG_ERR, "cannot bind iprop socket: %s",
e_txt);
if(oerrno == EADDRINUSE) {
char *w = strrchr(whoami, '/');
if (w) {
w++;
}
else {
w = whoami;
}
fprintf(stderr,
"This probably means that another %s process is already\n"
"running, or that another program is using the server port (number %d).\n"
"If another %s is already running, you should kill it before\n"
"restarting the server.\n",
w, ntohs(addr.sin_port), w);
}
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
#endif
transp = svctcp_create(s, 0, 0);
if(transp == NULL) {
fprintf(stderr, "%s: Cannot create RPC service.\n", whoami);
krb5_klog_syslog(LOG_ERR, "Cannot create RPC service: %m");
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
if(!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) {
fprintf(stderr, "%s: Cannot register RPC service.\n", whoami);
krb5_klog_syslog(LOG_ERR, "Cannot register RPC service, failing.");
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
#ifndef DISABLE_IPROP
iproptransp = svctcp_create(ipropfd, 0, 0);
if (iproptransp == NULL) {
fprintf(stderr, "%s: Cannot create RPC service.\n", whoami);
krb5_klog_syslog(LOG_ERR, "Cannot create RPC service: %m");
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
if (!svc_register(iproptransp, KRB5_IPROP_PROG, KRB5_IPROP_VERS, krb5_iprop_prog_1, IPPROTO_TCP)) {
fprintf(stderr, "%s: Cannot register RPC service.\n", whoami);
krb5_klog_syslog(LOG_ERR, "Cannot register RPC service, continuing.");
#if 0
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
#endif
}
#endif
names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm);
names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm);
names[2].name = build_princ_name(OVSEC_KADM_ADMIN_SERVICE, params.realm);
names[3].name = build_princ_name(OVSEC_KADM_CHANGEPW_SERVICE,
params.realm);
if (names[0].name == NULL || names[1].name == NULL ||
names[2].name == NULL || names[3].name == NULL) {
krb5_klog_syslog(LOG_ERR,
"Cannot build GSS-API authentication names, "
"failing.");
fprintf(stderr, "%s: Cannot build GSS-API authentication names.\n",
whoami);
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
hctx = ((kadm5_server_handle_t)global_server_handle)->context;
ret = krb5_ktkdb_set_context(hctx);
if (ret) {
krb5_klog_syslog(LOG_ERR, "Can't set kdb keytab's internal context.");
goto kterr;
}
ret = krb5_db_set_mkey(hctx, &master_keyblock);
if (ret) {
krb5_klog_syslog(LOG_ERR, "Can't set master key for kdb keytab.");
goto kterr;
}
ret = krb5_kt_register(context, &krb5_kt_kdb_ops);
if (ret) {
krb5_klog_syslog(LOG_ERR, "Can't register kdb keytab.");
goto kterr;
}
ret = krb5_gss_register_acceptor_identity("KDB:");
if (ret) {
krb5_klog_syslog(LOG_ERR, "Can't register acceptor keytab.");
goto kterr;
}
kterr:
if (ret) {
krb5_klog_syslog(LOG_ERR, "%s", krb5_get_error_message (context, ret));
fprintf(stderr, "%s: Can't set up keytab for RPC.\n", whoami);
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
if (svcauth_gssapi_set_names(names, 4) == TRUE)
oldnames++;
if (!oldnames && svcauth_gssapi_set_names(names, 2) == FALSE) {
krb5_klog_syslog(LOG_ERR,
"Cannot set GSS-API authentication names (keytab not present?), "
"failing.");
fprintf(stderr, "%s: Cannot set GSS-API authentication names.\n",
whoami);
svcauth_gssapi_unset_names();
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
in_buf.value = names[1].name;
in_buf.length = strlen(names[1].name) + 1;
(void) gss_import_name(&OMret, &in_buf, nt_krb5_name_oid,
&gss_changepw_name);
if (oldnames) {
in_buf.value = names[3].name;
in_buf.length = strlen(names[3].name) + 1;
(void) gss_import_name(&OMret, &in_buf, nt_krb5_name_oid,
&gss_oldchangepw_name);
}
svcauth_gssapi_set_log_badauth_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_badauth_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) {
fprintf(stderr, "%s: Cannot initialize RPCSEC_GSS service name.\n",
whoami);
exit(1);
}
pidfile = pidfile_open("/var/run/kadmind.pid", 0600, NULL);
if (pidfile == NULL) {
krb5_klog_syslog(LOG_ERR, "kadmind already running");
fprintf(stderr, "%s: kadmind already running\n", whoami);
svcauth_gssapi_unset_names();
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
if ((ret = kadm5int_acl_init(context, 0, params.acl_file))) {
errmsg = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR, "Cannot initialize acl file: %s",
errmsg);
fprintf(stderr, "%s: Cannot initialize acl file: %s\n",
whoami, errmsg);
svcauth_gssapi_unset_names();
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
pidfile_remove(pidfile);
exit(1);
}
if (!nofork && (ret = daemon(0, 0))) {
ret = errno;
errmsg = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR, "Cannot detach from tty: %s", errmsg);
fprintf(stderr, "%s: Cannot detach from tty: %s\n",
whoami, errmsg);
svcauth_gssapi_unset_names();
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
pidfile_remove(pidfile);
exit(1);
}
pidfile_write(pidfile);
krb5_klog_syslog(LOG_INFO, "Seeding random number generator");
ret = krb5_c_random_os_entropy(context, 1, NULL);
if (ret) {
krb5_klog_syslog(LOG_ERR, "Error getting random seed: %s, aborting",
krb5_get_error_message(context, ret));
svcauth_gssapi_unset_names();
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
pidfile_remove(pidfile);
exit(1);
}
if (params.iprop_enabled == TRUE)
ulog_set_role(hctx, IPROP_MASTER);
else
ulog_set_role(hctx, IPROP_NULL);
log_ctx = hctx->kdblog_context;
if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
if ((ret = ulog_map(hctx, params.iprop_logfile,
params.iprop_ulogsize, FKADMIND, db_args)) != 0) {
fprintf(stderr,
_("%s: %s while mapping update log (`%s.ulog')\n"),
whoami, error_message(ret), params.dbname);
krb5_klog_syslog(LOG_ERR,
_("%s while mapping update log (`%s.ulog')"),
error_message(ret), params.dbname);
krb5_klog_close(context);
pidfile_remove(pidfile);
exit(1);
}
if (nofork)
fprintf(stderr,
"%s: create IPROP svc (PROG=%d, VERS=%d)\n",
whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
#if 0
if (!svc_create(krb5_iprop_prog_1,
KRB5_IPROP_PROG, KRB5_IPROP_VERS,
"circuit_v")) {
fprintf(stderr,
_("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
whoami,
KRB5_IPROP_PROG, KRB5_IPROP_VERS);
krb5_klog_syslog(LOG_ERR,
_("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
KRB5_IPROP_PROG, KRB5_IPROP_VERS);
krb5_klog_close(context);
pidfile_remove(pidfile);
exit(1);
}
#endif
#if 0
if ((ret = kiprop_get_adm_host_srv_name(context,
params.realm,
&kiprop_name)) != 0) {
krb5_klog_syslog(LOG_ERR,
_("%s while getting IProp svc name, failing"),
error_message(ret));
fprintf(stderr,
_("%s: %s while getting IProp svc name, failing\n"),
whoami, error_message(ret));
krb5_klog_close(context);
pidfile_remove(pidfile);
exit(1);
}
auth_gssapi_name iprop_name;
iprop_name.name = build_princ_name(foo, bar);
if (iprop_name.name == NULL) {
foo error;
}
iprop_name.type = nt_krb5_name_oid;
if (svcauth_gssapi_set_names(&iprop_name, 1) == FALSE) {
foo error;
}
if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0,
KRB5_IPROP_PROG, KRB5_IPROP_VERS)) {
rpc_gss_error_t err;
(void) rpc_gss_get_error(&err);
krb5_klog_syslog(LOG_ERR,
_("Unable to set RPCSEC_GSS service name (`%s'), failing."),
kiprop_name ? kiprop_name : "<null>");
fprintf(stderr,
_("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
whoami,
kiprop_name ? kiprop_name : "<null>");
if (nofork) {
fprintf(stderr,
"%s: set svc name (rpcsec err=%d, sys err=%d)\n",
whoami,
err.rpc_gss_error,
err.system_error);
}
pidfile_remove(pidfile);
exit(1);
}
free(kiprop_name);
#endif
}
setup_signal_handlers(log_ctx->iproprole);
krb5_klog_syslog(LOG_INFO, _("starting"));
if (nofork)
fprintf(stderr, "%s: starting...\n", whoami);
kadm_svc_run(¶ms);
krb5_klog_syslog(LOG_INFO, "finished, exiting");
svcauth_gssapi_unset_names();
kadm5_destroy(global_server_handle);
close(s);
kadm5int_acl_finish(context, 0);
if(gss_changepw_name) {
(void) gss_release_name(&OMret, &gss_changepw_name);
}
if(gss_oldchangepw_name) {
(void) gss_release_name(&OMret, &gss_oldchangepw_name);
}
for(s = 0 ; s < 4; s++) {
if (names[s].name) {
free(names[s].name);
}
}
krb5_klog_close(context);
krb5_free_context(context);
pidfile_remove(pidfile);
exit(2);
}
void setup_signal_handlers(iprop_role iproprole) {
#ifdef POSIX_SIGNALS
(void) sigemptyset(&s_action.sa_mask);
s_action.sa_handler = request_exit;
(void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
(void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
(void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL);
s_action.sa_handler = request_hup;
(void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
s_action.sa_handler = sig_pipe;
(void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
#ifdef PURIFY
s_action.sa_handler = request_pure_report;
(void) sigaction(SIGUSR1, &s_action, (struct sigaction *) NULL);
s_action.sa_handler = request_pure_clear;
(void) sigaction(SIGUSR2, &s_action, (struct sigaction *) NULL);
#endif
if (iproprole == IPROP_MASTER) {
s_action.sa_handler = SIG_IGN;
(void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
}
#else
signal(SIGINT, request_exit);
signal(SIGTERM, request_exit);
signal(SIGQUIT, request_exit);
signal(SIGHUP, request_hup);
signal(SIGPIPE, sig_pipe);
#ifdef PURIFY
signal(SIGUSR1, request_pure_report);
signal(SIGUSR2, request_pure_clear);
#endif
if (iproprole == IPROP_MASTER)
(void) signal(SIGCHLD, SIG_IGN);
#endif
}
void kadm_svc_run(params)
kadm5_config_params *params;
{
fd_set rfd;
struct timeval timeout;
while(signal_request_exit == 0) {
if (signal_request_hup) {
reset_db();
krb5_klog_reopen(context);
signal_request_hup = 0;
}
#ifdef PURIFY
if (signal_pure_report)
{
purify_new_reports();
signal_pure_report = 0;
}
if (signal_pure_clear)
{
purify_clear_new_reports();
signal_pure_clear = 0;
}
#endif
timeout.tv_sec = TIMEOUT;
timeout.tv_usec = 0;
rfd = svc_fdset;
FD_SET(schpw, &rfd);
#define max(a, b) (((a) > (b)) ? (a) : (b))
switch(select(max(schpw, svc_maxfd) + 1,
(fd_set *) &rfd, NULL, NULL, &timeout)) {
case -1:
if(errno == EINTR)
continue;
perror("select");
return;
case 0:
reset_db();
break;
default:
if (FD_ISSET(schpw, &rfd))
do_schpw(schpw, params);
else
svc_getreqset(&rfd);
}
}
}
#ifdef PURIFY
void request_pure_report(int signum)
{
krb5_klog_syslog(LOG_DEBUG, "Got signal to request a Purify report");
signal_pure_report = 1;
return;
}
void request_pure_clear(int signum)
{
krb5_klog_syslog(LOG_DEBUG, "Got signal to request a Purify report and clear the old Purify info");
signal_pure_report = 1;
signal_pure_clear = 1;
return;
}
#endif
void request_hup(int signum)
{
signal_request_hup = 1;
return;
}
void reset_db(void)
{
#ifdef notdef
kadm5_ret_t ret;
char *errmsg;
if (ret = kadm5_flush(global_server_handle)) {
krb5_klog_syslog(LOG_ERR, "FATAL ERROR! %s while flushing databases. "
"Databases may be corrupt! Aborting.",
krb5_get_error_message (context, ret));
krb5_klog_close(context);
exit(3);
}
#endif
return;
}
void request_exit(int signum)
{
krb5_klog_syslog(LOG_DEBUG, "Got signal to request exit");
signal_request_exit = 1;
return;
}
void sig_pipe(int unused)
{
krb5_klog_syslog(LOG_NOTICE, "Warning: Received a SIGPIPE; probably a "
"client aborted. Continuing.");
return;
}
char *build_princ_name(char *name, char *realm)
{
char *fullname;
if (realm) {
if (asprintf(&fullname, "%s@%s", name, realm) < 0)
fullname = NULL;
} else
fullname = strdup(name);
return fullname;
}
void log_badverf(gss_name_t client_name, gss_name_t server_name,
struct svc_req *rqst, struct rpc_msg *msg, char
*data)
{
struct procnames {
rpcproc_t proc;
const char *proc_name;
};
static const struct procnames 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, "SETV4KEY_PRINCIPAL"},
{18, "CREATE_PRINCIPAL3"},
{19, "CHPASS_PRINCIPAL3"},
{20, "CHRAND_PRINCIPAL3"},
{21, "SETKEY_PRINCIPAL3"}
};
#define NPROCNAMES (sizeof (proc_names) / sizeof (struct procnames))
OM_uint32 minor;
gss_buffer_desc client, server;
gss_OID gss_type;
char *a;
rpcproc_t proc;
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 = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr);
proc = msg->rm_call.cb_proc;
procname = NULL;
for (i = 0; i < NPROCNAMES; 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);
}
void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg,
char *error, char *data)
{
char *a;
a = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr);
krb5_klog_syslog(LOG_NOTICE, "Miscellaneous RPC error: %s, %s", a, error);
}
void log_badauth(OM_uint32 major, OM_uint32 minor,
struct sockaddr_in *addr, char *data)
{
char *a;
a = inet_ntoa(addr->sin_addr);
krb5_klog_syslog(LOG_NOTICE, "Authentication attempt failed: %s, GSS-API "
"error strings are:", a);
log_badauth_display_status(" ", major, minor);
krb5_klog_syslog(LOG_NOTICE, " GSS-API error strings complete.");
}
void log_badauth_display_status(char *msg, OM_uint32 major, OM_uint32 minor)
{
log_badauth_display_status_1(msg, major, GSS_C_GSS_CODE, 0);
log_badauth_display_status_1(msg, minor, GSS_C_MECH_CODE, 0);
}
void log_badauth_display_status_1(char *m, OM_uint32 code, int type,
int rec)
{
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) {
if (!rec) {
log_badauth_display_status_1(m,gssstat,GSS_C_GSS_CODE,1);
log_badauth_display_status_1(m, minor_stat,
GSS_C_MECH_CODE, 1);
} else
krb5_klog_syslog(LOG_ERR, "GSS-API authentication error %.*s: "
"recursive failure!", (int) msg.length,
(char *) msg.value);
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;
}
}
void do_schpw(int s, kadm5_config_params *params)
{
krb5_error_code ret;
char req[1500];
int len;
struct sockaddr_in from, to;
socklen_t tolen;
krb5_keytab kt;
krb5_data reqdata, repdata;
if ((ret = krb5_kt_resolve(context, "KDB:", &kt))) {
krb5_klog_syslog(LOG_ERR, "chpw: Couldn't open admin keytab %s",
krb5_get_error_message (context, ret));
return;
}
memset(&to, 0, sizeof(to));
{
struct msghdr msg;
struct iovec iov;
char control[512];
struct cmsghdr *cmsg;
iov.iov_base = req;
iov.iov_len = sizeof(req);
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
msg.msg_name = (caddr_t)&from;
msg.msg_namelen = sizeof(from);
if ((len = recvmsg(s, &msg, 0)) < 0) {
krb5_klog_syslog(LOG_ERR, "chpw: Couldn't receive request: %s",
krb5_get_error_message (context, errno));
return;
}
#ifdef IP_RECVDSTADDR
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
if (cmsg->cmsg_len - sizeof(*cmsg) >= sizeof(to.sin_addr.s_addr)) {
memcpy(&to.sin_addr.s_addr, CMSG_DATA(cmsg), sizeof(to.sin_addr.s_addr));
break;
}
}
}
#endif
}
reqdata.length = len;
reqdata.data = req;
if (to.sin_addr.s_addr == 0) {
int s2;
if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
const char *errmsg = krb5_get_error_message (context, errno);
krb5_klog_syslog(LOG_ERR, "cannot create connecting socket: %s",
errmsg);
fprintf(stderr, "Cannot create connecting socket: %s",
errmsg);
svcauth_gssapi_unset_names();
kadm5_destroy(global_server_handle);
krb5_klog_close(context);
exit(1);
}
set_cloexec_fd(s2);
if (connect(s2, (struct sockaddr *) &from, sizeof(from)) < 0) {
krb5_klog_syslog(LOG_ERR, "chpw: Couldn't connect to client: %s",
krb5_get_error_message (context, errno));
close(s2);
goto cleanup;
}
tolen = sizeof(tolen);
memset(&to, 0, sizeof(to));
if (getsockname(s2, (struct sockaddr *)&to, &tolen) < 0)
goto cleanup;
if (to.sin_addr.s_addr == 0) {
krb5_address **addrs;
int i;
krb5_os_localaddr(context, &addrs);
for (i = 0; addrs[i] != NULL; i++) {
if (addrs[i]->addrtype == ADDRTYPE_INET) {
to.sin_family = AF_INET;
memcpy(&to.sin_addr, addrs[i]->contents, sizeof(to.sin_addr));
break;
}
}
krb5_free_addresses(context, addrs);
if (to.sin_addr.s_addr == 0) {
krb5_klog_syslog(LOG_ERR, "chpw: no address found");
goto cleanup;
}
}
close(s2);
}
if ((ret = process_chpw_request(context, global_server_handle,
params->realm, kt, &to, &from,
&reqdata, &repdata))) {
krb5_klog_syslog(LOG_ERR, "chpw: Error processing request: %s",
krb5_get_error_message (context, ret));
}
if (repdata.length == 0) {
goto cleanup;
}
len = sendto(s, repdata.data, (int) repdata.length, 0,
(struct sockaddr *) &from, sizeof(from));
if (len < (int) repdata.length) {
krb5_xfree(repdata.data);
krb5_klog_syslog(LOG_ERR, "chpw: Error sending reply: %s",
krb5_get_error_message (context, errno));
goto cleanup;
}
krb5_xfree(repdata.data);
cleanup:
krb5_kt_close(context, kt);
return;
}