ovsec_kadmd.c revision 78894ffc7b2ee149add8332a811ec7f43fd945c1
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* 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 <kadm5/kadm_rpc.h>
#include <server_acl.h>
#include <krb5/adm_proto.h>
#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"
#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;
static int schpw;
void kadm_svc_run(void);
void sig_exit(int);
void sig_pipe(int);
krb5_error_code log_kt_error(char*, char*);
#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"
/*
* This enables us to set the keytab that gss_acquire_cred uses, but
* it also restricts us to linking against the Kv5 GSS-API library.
* Since this is *k*admind, that shouldn't be a problem.
*/
extern char *krb5_overridekeyname;
extern void krb5_iprop_prog_1();
const char *,
char **);
static krb5_context hctx;
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);
}
}
int
{
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;
int retdn;
int iprop_supported;
/* 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);
}
#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);
}
/* SUNW14resync */
#if 0
if(ret) {
error_message(ret));
exit(1);
}
#endif
/*
* password changing, the default port is 464
* (officially recognized by IANA)
*
* DEFAULT_KPASSWD_PORT -> 464
*/
&chgpw_params)) {
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
*/
exit(1);
}
char portbuf[32];
if(oerrno == EADDRINUSE) {
if (w) {
w++;
}
else {
w = whoami;
}
"This probably means that another %s process\n"
"is already running, or that another program\n"
"is using the server port (number %d).\n"
"If another %s is already running, you should\n"
"kill it before restarting the server.\n"),
}
exit(1);
}
¶ms)) {
gettext("%s: %s while initializing, aborting\n"),
exit(1);
}
gettext("%s: Missing required configuration values "
"while initializing, aborting"), whoami,
gettext("%s: Missing required configuration values "
"(%lx) while initializing, aborting\n"), whoami,
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 deamon. 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"),
gettext("Check for already-running %s or for "
"another process using port %d"), w,
}
exit(1);
}
whoami);
exit(1);
}
gettext("Cannot register RPC service, failing."));
exit(1);
}
/*
* XXX krb5_defkeyname is an internal library global and should go
* away
*/
/* 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.
*/
(void) kadm5_get_adm_host_srv_name(context,
(void) kadm5_get_cpw_host_srv_name(context,
gettext("Cannot initialize GSS-API authentication, "
"failing."));
gettext("%s: Cannot initialize "
"GSS-API authentication.\n"),
whoami);
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++;
else
oldnames++;
else
/*
* 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) {
}
error_message(ret));
exit(1);
}
&global_server_handle)) != KADM5_OK) {
gettext("%s while initializing, aborting"),
error_message(ret));
gettext("%s: %s while initializing, aborting\n"),
exit(1);
}
gettext("Cannot detach from tty: %s"),
error_message(ret));
exit(1);
}
if( db_args )
{
}
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);
/* Try to determine if the error was caused by a missing keytab or
* missing keytab entries (and log it).
*/
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: sig_exit
*
* Purpose: sets flags saying the server got a signal and that it
* should exit when convenient.
*
* Effects:
* Modifies signal_request_exit which ideally makes the server exit
* at some point.
*
* Modifies:
* Signal_request_exit
*/
{
signal_request_exit = 1;
return;
}
/*
* Function: sig_pipe
*
* Purpose: SIGPIPE handler
*
* Effects: krb5_klog_syslog 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.
*/
void
{
"probably a client aborted. Continuing."));
}
/*
* Given a service name (s_name) determine if the keytab file exists
* and if the keytab entry is present. Log missing keytab
* at LOG_ERR and log missing keytab entries at LOG_WARNING.
* If any of krb5_* (or strdup) fail it will return the failure.
*/
krb5_error_code code = 0;
char kt_name[MAX_KEYTAB_NAME_LEN];
char *service;
char *host;
if(!service)
return ENOMEM;
*host++ = '\0';
gettext("krb5_sname_to_principal failed: %s"),
gettext("%s: krb5_sname_to_principal failed: %s"),
return code;
}
gettext("krb5_kt_default_name failed: %s"),
gettext("%s: krb5_kt_default_name failed: %s"),
return code;
}
gettext("krb5_kt_default failed: %s"),
gettext("%s: krb5_kt_default failed: %s"),
return code;
}
switch (code) {
case 0:
break;
case KRB5_KT_NOTFOUND:
gettext("Keytab entry \"%s/%s\" is missing from \"%s\""),
kt_name);
gettext("%s: Keytab entry \"%s/%s\" is missing from \"%s\".\n"),
kt_name);
break;
case ENOENT:
gettext("Keytab file \"%s\" does not exist"),
kt_name);
gettext("%s: Keytab file \"%s\" does not exist.\n"),
kt_name);
break;
}
return code;
}