ovsec_kadmd.c revision d15b099249c60b5306559a0f81a5def6e23485c4
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
*
* Openvision retains the copyright to derivative works of
* this source code. Do *NOT* create a derivative of this
* source code before consulting with your legal department.
* Do *NOT* integrate *ANY* of this source code into another
* product before consulting with your legal department.
*
* For further information, read the top-level Openvision
* copyright which is contained in the top-level MIT Kerberos
* copyright.
*
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
*
*/
/*
* 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.
*/
/*
* SUNWresync121 XXX
* Beware future resyncers, this file is much diff from MIT (1.0...)
*/
#include <stdio.h>
#include <stdio_ext.h>
#include <signal.h>
#include <syslog.h>
#ifdef _AIX
#endif
#include <unistd.h>
#include "gssapiP_krb5.h" /* for kg_get_context */
#include <kadm5/kadm_rpc.h>
#include <server_acl.h>
#include <krb5/adm_proto.h>
#include "kdb_kt.h" /* for krb5_ktkdb_set_context */
#include <string.h>
#include <kadm5/server_internal.h>
#include <gssapi_krb5.h>
#include <libintl.h>
#include <locale.h>
#include <sys/resource.h>
#include <kdb_kt.h>
#include <rpc/rpcsec_gss.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 /* PURIFY */
#ifndef FD_SETSIZE
#define FD_SETSIZE 256
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#if defined(NEED_DAEMON_PROTO)
extern int daemon(int, int);
#endif
static int signal_request_exit = 0;
void request_exit(int);
void sig_pipe(int);
void kadm_svc_run(void);
#ifdef POSIX_SIGNALS
#endif /* POSIX_SIGNALS */
#define TIMEOUT 15
typedef struct _auth_gssapi_name {
char *name;
void *global_server_handle;
/*
* This is a kludge, but the server needs these constants to be
* but only if USE_KADM5_API_VERSION == 1.
*/
#define OVSEC_KADM_ADMIN_SERVICE_P "ovsec_adm@admin"
#define OVSEC_KADM_CHANGEPW_SERVICE_P "ovsec_adm@changepw"
extern void krb5_iprop_prog_1();
const char *,
char **);
static int schpw;
int nofork = 0; /* global; don't fork (debug mode) */
/*
* Function: usage
*
* Purpose: print out the server usage message
*
* Arguments:
* Requires:
* Effects:
* Modifies:
*/
static void usage()
{
"[-p port-number]\n"));
exit(1);
}
/*
* Function: display_status
*
* Purpose: displays GSS-API messages
*
* Arguments:
*
* msg a string to be displayed with the message
* maj_stat the GSS-API major status code
* min_stat the GSS-API minor status code
*
* Effects:
*
* The GSS-API messages associated with maj_stat and min_stat are
* displayed on stderr, each preceeded by "GSS-API error <msg>: " and
* followed by a newline.
*/
static void display_status_1(char *, OM_uint32, int);
char *msg;
{
}
char *m;
int type;
{
msg_ctx = 0;
while (1) {
if (!msg_ctx)
break;
}
}
/*
* Solaris Kerberos: the following prototypes are needed because these are
* private interfaces that do not have prototypes in any .h
*/
extern void res_freehostent(struct hostent *);
static void
freedomnames(char **npp)
{
char **tpp;
if (npp) {
while (*tpp++) {
}
}
}
/*
* Construct a list of uniq FQDNs of all the net interfaces (except
* krb5.conf master dups) and return it in arg 'dnames'.
*
* On successful return (0), caller must call freedomnames()
* to free memory.
*/
static int
{
krb5_address *a = NULL;
int dup=0, n = 0;
return (ret);
}
if (ret != 0) {
if (nofork)
"kadmind: get localaddrs failed: %s",
error_message(ret));
goto err;
}
for (i=0; addresses[i]; i++) {
a = addresses[i];
a->addrtype == ADDRTYPE_INET
&error);
/* skip master host in krb5.conf */
continue;
}
dup = 0;
/* skip if hostname already exists in list */
dup++;
break;
}
}
if (dup) {
continue;
}
if (!npp) {
goto err;
}
if (!npp[n]) {
goto err;
}
n++;
result = 0;
}
}
#ifdef DEBUG
}
#endif
goto out;
err:
if (npp) {
}
if (hp) {
}
out:
if (cfhost) {
}
if (addresses) {
}
if (result == 0)
return (result);
}
/*
* Set the rpcsec_gss svc names for all net interfaces.
*/
static void
{
if (!tpp)
return;
while (*tpp++) {
/* MAX_NAME_LEN from rpc/rpcsec_gss.h */
"kerberos_v5", 0,
"rpc_gss_set_svc_name success: %s\n",
name);
}
}
/* XXX yuck. the signal handlers need this */
static krb5_context context;
static krb5_context hctx;
{
extern char *optarg;
char *whoami;
struct sockaddr_in addr;
struct sockaddr_in *sin;
int s;
int optchar;
void *handlep;
int fd;
char reqbuf[128];
int db_args_size = 0;
const char *errmsg;
int retdn;
int iprop_supported;
/* Solaris Kerberos: Stores additional error messages */
/* Solaris Kerberos: Indicates whether loalhost is master or not */
/* Solaris Kerberos: Used for checking acl file */
/* This is OID value the Krb5_Name NameType */
if (major_status != GSS_S_COMPLETE) {
gettext("Couldn't create KRB5 Name NameType OID\n"));
exit(1);
}
#ifdef PURIFY
#endif /* PURIFY */
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
nofork = 0;
switch (optchar) {
case 'r':
if (!optarg)
usage();
break;
case 'm':
break;
case 'd':
nofork = 1;
break;
case 'p':
if (!optarg)
usage();
break;
case 'x':
if (!optarg)
usage();
db_args_size++;
{
{
whoami);
exit(1);
}
}
break;
case '?':
default:
usage();
}
}
}
gettext("%s: %s while initializing context, aborting\n"),
exit(1);
}
/* Solaris Kerberos */
gettext("%s while initializing, aborting"),
gettext("%s: %s while initializing, aborting\n"),
if (emsg)
exit(1);
}
if( db_args )
{
}
¶ms))) {
/* Solaris Kerberos: Remove double "whoami" */
e_txt);
gettext("%s: %s while initializing, aborting\n"),
exit(1);
}
/* Solaris Kerberos: Keep error messages consistent */
gettext("Missing required configuration values (%lx)"
"while initializing, aborting"),
gettext("%s: Missing required configuration values "
"(%lx) while initializing, aborting\n"), whoami,
exit(1);
}
/*
* password changing, the default port is 464
* (officially recognized by IANA)
*
* DEFAULT_KPASSWD_PORT -> 464
*/
&chgpw_params)) {
/* Solaris Kerberos: Remove double "whoami" */
gettext("%s: %s while initializing, aborting\n"),
exit(1);
}
/*
* We now setup the socket and bind() to port 464, so that
* kadmind can now listen to and process change-pwd requests
* from non-Solaris Kerberos V5 clients such as Microsoft,
* MIT, AIX, HP etc
*/
gettext( "Cannot create simple " "chpw socket: %s"),
e_txt);
e_txt);
exit(1);
}
/* Solaris Kerberos: Ensure that kadmind is only run on a master kdc */
gettext("Failed to determine whether host is master "
error_message(ret));
gettext("%s: Failed to determine whether host is master "
error_message(ret));
exit(1);
}
gettext("%s can only be run on the master KDC, %s, for "
gettext("%s: %s can only be run on the master KDC, %s, for "
exit(1);
}
(void) krb5_klog_syslog(LOG_ERR,
gettext("cannot get any transport information"));
exit(1);
}
break;
}
(void) endnetconfig(handlep);
exit(1);
}
if (fd == -1) {
gettext("unable to open connection for ADMIN server"));
exit(1);
}
/* LINTED */
/*
* The option value is "1". This will allow the server to restart
* whilst the previous process is cleaning up after itself in a
* FIN_WAIT_2 or TIME_WAIT state. If another process is started
* outside of smf(5) then bind will fail anyway, which is what we want.
*/
t_error("t_optmgmt");
exit(1);
}
/* Transform addr to netbuf */
(void) krb5_klog_syslog(LOG_ERR,
gettext("cannot allocate netbuf"));
exit(1);
}
/* SUNWresync121 XXX (void) memset(&addr, 0, sizeof(addr)); */
if(oerrno == EADDRINUSE) {
if (w) {
w++;
}
else {
w = whoami;
}
"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"
"another process using port %d"), w,
}
exit(1);
}
char portbuf[32];
e_txt);
if(oerrno == EADDRINUSE) {
if (w) {
w++;
}
else {
w = whoami;
}
"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"),
}
exit(1);
}
(void) endnetconfig(handlep);
exit(1);
}
exit(1);
}
/* Solaris Kerberos:
* The only service principals which matter here are
* -> names[0].name (kadmin/<fqdn>)
* -> names[1].name (changepw/<fqdn>)
* KADM5_ADMIN_SERVICE_P, KADM5_CHANGEPW_SERVICE_P,
* OVSEC_KADM_ADMIN_SERVICE_P, OVSEC_KADM_CHANGEPW_SERVICE_P
* are all legacy service princs and calls to rpc_gss_set_svc_name()
* using these principals will always fail as they are not host
* based principals.
*/
gettext("Cannot get host based service name for admin "
error_message(ret));
gettext("%s: Cannot get host based service name for admin "
error_message(ret));
exit(1);
}
gettext("Cannot get host based service name for changepw "
error_message(ret));
gettext("%s: Cannot get host based service name for "
error_message(ret));
exit(1);
}
gettext("Cannot initialize GSS-API authentication, "
"failing."));
gettext("%s: Cannot initialize "
"GSS-API authentication.\n"),
whoami);
exit(1);
}
/*
* Go through some contortions to point gssapi at a kdb keytab.
* This prevents kadmind from needing to use an actual file-based
* keytab.
*/
/* XXX extract kadm5's krb5_context */
/* Set ktkdb's internal krb5_context. */
if (ret) {
goto kterr;
}
/* Solaris Kerberos */
if (ret) {
goto kterr;
}
if (ret) {
goto kterr;
}
/* Tell gssapi about the kdb keytab. */
if (ret) {
goto kterr;
}
if (ret) {
exit(1);
}
/*
* Try to acquire creds for the old OV services as well as the
* new names, but if that fails just fall back on the new names.
*/
oldnames++;
oldnames++;
oldnames++;
/* If rpc_gss_set_svc_name() fails for either kadmin/<fqdn> or
* for changepw/<fqdn> then try to determine if this is caused
* by a missing keytab file or entry. If so, log it and continue.
*/
oldnames++;
oldnames++;
/*
* Multi-homed KDCs sometimes may need to set svc names
* for multiple net interfaces so we set them for
* all interfaces just in case.
*/
}
/* if set_names succeeded, this will too */
if (oldnames) {
}
errmsg);
exit(1);
}
/*
* Solaris Kerberos:
* Warn if the acl file contains an entry for a principal matching the
* default (unconfigured) acl rule.
*/
/* Use any value as the first component - 'x' in this case */
== GSS_S_COMPLETE) {
gettext("acls may not be properly configured: "
"found an acl matching \"___default_realm___\" in "
"acls may not be properly configured: found an acl "
"matching \"___default_realm___\" in %s\n"),
}
}
/*
* Solaris Kerberos:
* List the logs (FILE, STDERR, etc) which are currently being
* logged to and print to stderr. Useful when trying to
* track down a failure via SMF.
*/
error_message(ret));
}
exit(1);
}
/* SUNW14resync */
#if 0
if (ret) {
exit(1);
}
#endif
gettext("%s: %s while trying to determine if KDB "
"plugin supports iprop\n"), whoami,
error_message(ret));
gettext("%s while trying to determine if KDB "
exit(1);
}
if (!iprop_supported) {
gettext("%s: Warning, current KDB "
"plugin does not support iprop, continuing "
"with iprop disabled\n"), whoami);
gettext("Warning, current KDB "
"plugin does not support iprop, continuing "
"with iprop disabled"));
} else
} else
/*
* IProp is enabled, so let's map in the update log
* and setup the service.
*/
gettext("%s: %s while mapping update log "
gettext("%s while mapping update log "
exit(1);
}
if (nofork)
"%s: create IPROP svc (PROG=%d, VERS=%d)\n",
if (!svc_create(krb5_iprop_prog_1,
"circuit_v")) {
gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
exit(1);
}
&kiprop_name)) {
gettext("%s while getting IProp svc name, failing"),
error_message(ret));
gettext("%s: %s while getting IProp svc name, failing\n"),
exit(1);
}
(void) rpc_gss_get_error(&err);
gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."),
gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
if (nofork) {
"%s: set svc name (rpcsec err=%d, sys err=%d)\n",
}
exit(1);
}
}
} else {
if (!oldnames) {
/* rpc_gss_set_svc_name failed for both kadmin/<fqdn> and
* changepw/<fqdn>.
*/
gettext("Unable to set RPCSEC_GSS service names "
"('%s, %s')"),
gettext("%s: Unable to set RPCSEC_GSS service names "
"('%s, %s')\n"),
exit(1);
}
}
if (dnames)
if (nofork)
/*
* We now call our own customized async event processing
* function kadm_svc_run(), as opposed to svc_run() earlier,
* non-RPCSEC_GSS based change-pwd requests apart from the
* regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients.
*/
kadm_svc_run();
exit(0);
}
/*
* Function: kadm_svc_run
*
* Purpose: modified version of sunrpc svc_run.
* which closes the database every TIMEOUT seconds.
*
* Arguments:
* Requires:
* Effects:
* Modifies:
*/
void kadm_svc_run(void)
{
int pollret;
int nfds = 0;
int i;
while(signal_request_exit == 0) {
if (nfds != svc_max_pollfd) {
}
sizeof (pollfd_t) * svc_max_pollfd);
for (i = 0; i < nfds; i++) {
break;
}
}
__rpc_timeval_to_msec(&timeout))) {
case -1:
continue;
perror("poll");
return;
case 0:
continue;
default:
for (i = 0; i < nfds; i++) {
&chgpw_params);
else
break;
} else {
if (i == (nfds - 1))
perror("poll");
}
}
break;
}
}
}
/*
* Function: setup_signal_handlers
*
* Purpose: Setup signal handling functions with System V's signal().
*/
/*
* IProp will fork for a full-resync, we don't want to
* wait on it and we don't want the living dead procs either.
*/
if (iproprole == IPROP_MASTER)
return;
}
/*
* Function: request_exit
*
* Purpose: sets flags saying the server got a signal and that it
* should exit when convient.
*
* Arguments:
* Requires:
* Effects:
* modifies signal_request_exit which ideally makes the server exit
* at some point.
*
* Modifies:
* signal_request_exit
*/
void request_exit(int signum)
{
signal_request_exit = 1;
return;
}
/*
* Function: sig_pipe
*
* Purpose: SIGPIPE handler
*
* Effects: krb5_klog_syslogs a message that a SIGPIPE occurred and returns,
* thus causing the read() or write() to fail and, presumable, the RPC
* to recover. Otherwise, the process aborts.
*/
{
"probably a client aborted. Continuing."));
return;
}