/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley
* under license from the Regents of the University of
* California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ypsym.h"
#include <stdlib.h>
#include "yp_b.h"
#include <string.h>
#include <limits.h>
#include <netconfig.h>
#include <netdir.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/systeminfo.h>
#ifndef NULL
#define NULL 0
#endif
void ypbind_init_default();
static int ypbind_pipe_setdom();
extern struct netconfig *__rpc_getconf();
extern void *__rpc_setconf(), *__rpc_endconf();
extern CLIENT *__clnt_tp_create_bootstrap();
extern char *inet_ntoa();
extern int __rpc_get_local_uid();
extern listofnames *names();
extern void free_listofnames();
static void broadcast_setup();
static void sigcld_handler();
static struct ypbind_binding *dup_ypbind_binding();
static struct netbuf *dup_netbuf();
static void free_ypbind_binding();
static void enable_exit();
static void ypbind_ping();
static struct domain *ypbind_point_to_domain();
static bool ypbind_broadcast_ack();
static int pong_servers();
void cache_binding();
void uncache_binding();
extern int setok;
extern int broadcast;
extern int cache_okay;
/*
* Need to differentiate between RPC_UNKNOWNHOST returned by the RPC
* library, and the same error caused by a local lookup failure in
*/
int hostNotKnownLocally;
/*ARGSUSED*/
void *
void *argp;
{
static char res;
return ((void *) & res);
}
static void
{
if (!done) {
}
}
int sigcld_event = 0;
static void
{
sigcld_event++;
#ifdef DEBUG
#endif
}
/*
* This is a Unix SIGCHILD handler that notices when a broadcaster child
* process has exited, and retrieves the exit status. The broadcaster pid
* is set to 0. If the broadcaster succeeded, dom_report_success will be
* be set to -1.
*/
void
{
sigcld_event = 0;
/* ==== Why WEXITED? */
case CLD_EXITED:
break;
case CLD_KILLED:
case CLD_DUMPED:
break;
case CLD_TRAPPED:
case CLD_STOPPED:
case CLD_CONTINUED:
enable_exit();
return;
}
#ifdef DEBUG
"ypbind event_handler: got wait from %d status = %d\n",
#endif
/* to aid the progeny print the infamous "not responding" message */
#ifdef DEBUG
#endif
if (succeeded) {
}
if (pdom->broadcaster_pipe != 0) {
pdom->broadcaster_pipe = 0;
}
pdom->dom_broadcaster_pid = 0;
break;
}
}
} /* while loop */
enable_exit();
}
static void
{
if (pdom->broadcaster_pipe) {
#ifdef DEBUG
#endif
}
#ifdef DEBUG
else {
}
#endif
}
#ifdef DEBUG
else {
}
#endif
}
/* Same as the ypbind_get_binding() routine in SunOS */
/*ARGSUSED*/
{
int bpid;
#ifdef DEBUG
#endif
return (&resp);
}
if (cur_domain->dom_boundp) {
#ifdef DEBUG
#endif
(void) ypbind_ping(cur_domain);
}
}
/*
* Bound or not, return the current state of the binding.
*/
if (cur_domain->dom_boundp) {
#ifdef DEBUG
#endif
} else {
#ifdef DEBUG
#endif
}
} else {
}
/*
* RETURN NOW: if successful, otherwise
* RETURN LATER: after spawning off a child to do the "broadcast" work.
*/
#ifdef DEBUG
#endif
return (&resp);
}
/* Go about the broadcast (really, pinging here) business */
(!cur_domain->dom_broadcaster_pid)) {
#ifdef DEBUG
#endif
/*
* The current domain is unbound, and there is no child
* process active now. Fork off a child who will beg to the
* ypservers list one by one or broadcast and accept whoever
* commands the right domain.
*/
#ifdef DEBUG
#endif
return (&resp);
}
enable_exit();
if (bpid != 0) { /* parent */
if (bpid > 0) { /* parent started */
if (cur_domain->broadcaster_pipe)
#ifdef DEBUG
#endif
/* remove it from ypbind's signal mask */
return (&resp);
} else { /* fork failed */
perror("fork");
#ifdef DEBUG
#endif
return (&resp);
}
} /* end parent */
/* child only code */
if (cur_domain->broadcaster_pipe)
else {
perror("fdopen-pipe");
exit(-1);
}
}
#ifdef DEBUG
#endif
return (&resp);
}
/*
* call ypbindproc_domain_3 and convert results
*
* This adds support for YP clients that send requests on
* ypbind version 1 & 2 (i.e. clients before we started
* using universal addresses and netbufs). This is supported
* for binary compatibility for static 4.x programs. The
* assumption used to be that clients coming in with ypbind vers 1
* should be given the address of a server serving ypserv version 1.
* However, since yp_bind routines in 4.x YP library try
* to connect with ypserv version 2, even if they requested
* binding using ypbind version 1, the ypbind process will
* "always" look for only ypserv version 2 servers for all
* (ypbind vers 1, 2, & 3) clients.
*/
{
return (NULL);
sin = (struct sockaddr_in *)
} else {
}
} else {
}
return (&resp);
}
/* used to exchange information between pong_servers and ypbind_broadcast_ack */
int
{
char *servername;
void *handle;
int nconf_count;
long inforet;
/*
* If the ``domain'' name passed in is not the same as the RPC
* domain set from /etc/defaultdomain. Then we set ``firsttime''
* to TRUE so no error messages are ever syslog()-ed this
* prevents a possible Denial of Service attack.
*/
if (broadcast) {
#ifdef DEBUG
#endif
/*
* Here we do the real SunOS thing that users love. Do a
* broadcast on the network and find out the ypserv. No need
* recursion looking up the server's IP address.
*/
stat == RPC_NOBROADCAST ||
stat == RPC_N2AXLATEFAILURE) {
clnt_sperrno(stat));
exit(-1);
}
if (domain_struct->broadcaster_pipe == 0)
/* init binding case */
if (domain_struct->dom_boundp) {
if (domain_struct->dom_report_success > 0)
"NIS server for domain \"%s\" OK", domain);
"NIS server not responding for domain \"%s\"; still trying", domain);
return (res);
}
#ifdef DEBUG
#endif
/*
* Do the politically correct thing.. transport independent and
* secure (trusts only listed servers).
*/
/*
* get list of possible servers for this domain
*/
/*
* get alias for domain: Things of the past..
* sysvconfig();
* (void) yp_getalias(domain, domain_alias, NAME_MAX);
*/
#ifdef DEBUG
#endif
return (-1);
}
if (servername == NULL) continue;
/* Check all datagram_v transports for this server */
return (-1);
}
nconf_count = 0;
clnt2 = 0;
nconf_count++;
/*
* We use only datagram here. It is expected to be udp.
* VERY IMPORTANT: __clnt_tp_create_bootstrap is a
* hacked up version that does not do netdir_getbyname.
*/
hostNotKnownLocally = 0;
clnt2 =
}
if (nconf_count == 0) {
return (-1);
}
if (clnt2 == 0) {
"NIS server %s is not in local host files !", servername);
}
clnt_pcreateerror("ypbind");
continue;
}
if (isok) {
if (domain_struct->dom_report_success > 0) {
"NIS server for domain \"%s\" OK", domain);
}
if (domain_struct->broadcaster_pipe == 0) {
/* init binding case --parent */
struct ypbind_binding *b =
setnc =
if (b == NULL) {
/* ASSERT: This shouldn't happen ! */
b =
domain_struct->dom_binding = b;
if (b == NULL) {
return (-2);
}
}
b->ypbind_nconf = setnc;
(char *)&setua);
if (b->ypbind_svcaddr) {
if (b->ypbind_svcaddr->buf)
free(b->ypbind_svcaddr);
}
if (b->ypbind_servername)
free(b->ypbind_servername);
b->ypbind_servername =
b->ypbind_hi_vers = YPVERS;
b->ypbind_lo_vers = YPVERS;
return (0);
}
return (res);
} else {
"server %s doesn't serve domain %s\n",
servername, domain);
}
} else {
}
}
/*
* We tried all servers, none obliged !
* After ypbind is started up it will not be bound
* immediately. This is normal, no error message
* is needed. Although, with the ypbind_init_default
* it will be bound immediately.
*/
"NIS server not responding for domain \"%s\"; still trying", domain);
}
return (-2);
}
struct netbuf *
{
return (NULL);
if ((outbuf =
return (NULL);
return (NULL);
}
return (outbuf);
}
/*
* This is called by the broadcast rpc routines to process the responses
* coming back from the broadcast request. Since the form of the request
* which is used in ypbind_broadcast_bind is "respond only in the positive
* case", we know that we have a server.
* The internet address of the responding server will be picked up from
* the saddr parameter, and stuffed into the domain. The domain's boundp
* field will be set TRUE. The first responding server (or the first one
* which is on a reserved port) will be the bound server for the domain.
*/
bool
bool *ptrue;
{
struct ypbind_binding b;
b.ypbind_nconf = nconf;
b.ypbind_svcaddr = nbuf;
b.ypbind_servername = "\000";
b.ypbind_hi_vers = YPVERS;
b.ypbind_lo_vers = YPVERS;
return (TRUE);
}
/*
* WARNING: This routine is entered only by the child process.
* Called if it pongs/broadcasts okay.
*/
static int
char *servername;
char *domain;
struct domain *opaque_domain;
{
int retval;
#ifdef DEBUG
#endif
/* ypbind_broadcast_ack already setup dom_binding for us */
} else if (client) {
#ifdef DEBUG
#endif
#ifdef DEBUG
#endif
return (-2);
}
/*
* Let's hardcode versions, that is the only ypserv we support anyway.
* Avoid the song and dance of recursively calling ypbind_ping
* for no reason. Consistent with the 4.1 policy, that if ypbind gets
* a request on new binder protocol, the requestor is looking for the
* new ypserv. And, we have even higher binder protocol version i.e. 3.
*/
} else
return (-1);
#ifdef DEBUG
" saving server settings, \nsupports versions %d thru %d\n",
#endif
if (opaque_domain->broadcaster_pipe == 0) {
#ifdef DEBUG
#endif
return (-2);
}
#ifdef DEBUG
#endif
/*
* This child process is about to exit. Don't bother freeing memory.
*/
if (!retval) {
#ifdef DEBUG
"YPBIND pipe_setdom failed \n(xdr failure) to server %s\n",
#endif
return (-3);
}
#ifdef DEBUG
#endif
return (0);
}
/* Same as ypbind_set_binding in SunOS */
/*
* We use a trick from SunOS to return an error to the ypset command
* when we are not allowing the domain to be set. We do a svcerr_noprog()
* to send RPC_PROGUNAVAIL to ypset. We also return NULL so that
* our caller (ypbindprog_3) won't try to return a result. This
* hack is necessary because the YPBINDPROC_SETDOM procedure is defined
* in the protocol to return xdr_void, so we don't have a direct way to
* return an error to the client.
*/
/*ARGSUSED*/
void *
{
if (transp) {
return (0);
}
return (&res);
}
/* find out who originated the request */
char *uaddr;
return (0);
}
} else {
}
/* for -ypset, it falls through and let anybody do a setdom ! */
"ypset request from %s not on loopback, \
if (uaddr)
return (0);
}
switch (setok) {
case YPSETNONE:
NC_LOOPBACK) == 0)
"ypset request to %s from %s failed - ypset not allowed",
if (uaddr)
return (0);
case YPSETLOCAL:
&caller_uid) < 0) {
unidentified local user on %s - ypset not allowed",
if (uaddr)
return (0);
}
if (caller_uid != 0) {
"Set domain request to host %s \
from local non-root user %ld failed - ypset not allowed",
if (uaddr)
return (0);
}
}
}
if (uaddr)
}
/* setting binding; old may be invalid */
/* this does the set -- should copy the structure */
if ((a_domain->dom_binding =
"dup_ypbind_binding failed\n");
if (transp) {
return (0);
}
return (&res);
}
#ifdef DEBUG
#endif
}
return (&res);
}
/*
* This returns a pointer to a domain entry. If no such domain existed on
* the list previously, an entry will be allocated, initialized, and linked
* to the list. Note: If no memory can be malloc-ed for the domain structure,
* the functional value will be (struct domain *) NULL.
*/
static struct domain *
register char *pname;
{
return (pdom);
}
/* Not found. Add it to the list */
"ypbind_point_to_domain: strdup failed\n");
return (NULL);
}
pdom->dom_broadcaster_pid = 0;
pdom->broadcaster_pipe = 0;
/*
* We don't give an error if pdom->cache_file is not set.
* If we got null (out of memory), then we just won't use
* the cache file in cache_binding() (assuming the
* application gets that far.
*/
}
else
return (pdom);
}
static void
{
int vers;
int isok;
return;
}
perror("clnt_tli_create");
clnt_pcreateerror("ypbind_ping()");
return;
}
#ifdef DEBUG
#endif
timeout) == RPC_SUCCESS) {
#ifdef DEBUG
"Server pinged successfully, supports versions %d thru %d\n",
#endif
} else {
}
if (pdom->dom_boundp)
}
static struct ypbind_binding *
struct ypbind_binding *a;
{
struct ypbind_binding *b;
int i;
if (b == NULL)
return (b);
b->ypbind_hi_vers = a->ypbind_hi_vers;
b->ypbind_lo_vers = a->ypbind_lo_vers;
b->ypbind_servername =
ncb = (b->ypbind_nconf =
nxb = (b->ypbind_svcaddr =
nca = a->ypbind_nconf;
nxa = a->ypbind_svcaddr;
ncb->nc_protofmly =
if (ncb->nc_protofmly)
if (nxb)
if (ncb)
if (b->ypbind_servername)
free(b->ypbind_servername);
if (b)
free(b);
return (NULL);
}
for (i = 0; i < nca->nc_nlookups; i++)
ncb->nc_lookups[i] =
for (i = 0; i < 8; i++)
for (i = 0; i < nca->nc_nlookups; i++)
if (ncb->nc_lookups[i])
if (ncb->nc_protofmly)
if (nxb)
if (ncb)
if (b->ypbind_servername)
free(b->ypbind_servername);
if (b)
free(b);
return (NULL);
}
return (b);
}
static void
struct ypbind_binding *b;
{
if (b == NULL)
return;
free(b->ypbind_servername);
free(b);
}
/*
* Preloads teh default domain's domain binding. Domain binding for the
* local node's default domain for ypserv version 2 (YPVERS) will be
* set up. This may make it a little slower to start ypbind during
* boot time, but would make it easy on other domains that rely on
* this binding.
*/
void
{
abort();
}
(void) pong_servers(cur_domain);
}
}
{
return (FALSE);
return (FALSE);
return (TRUE);
}
{
return (FALSE);
switch (objp->ypbind_status) {
case YPBIND_FAIL_VAL:
return (FALSE);
break;
case YPBIND_SUCC_VAL:
if (!xdr_ypbind_binding_2(xdrs,
return (FALSE);
break;
default:
return (FALSE);
}
return (TRUE);
}
/*
* The following is some caching code to improve the performance of
* yp clients. In the days of yore, a client would talk to rpcbind
* to get the address for ypbind, then talk to ypbind to get the
* address of the server. If a lot of clients are doing this at
* the same time, then rpcbind and ypbind get bogged down and clients
* start to time out.
*
* We cache two things: the current address for ypserv, and the
* transport addresses for talking to ypbind. These are saved in
* a file and reads the address. It does not have to talk to rpcbind
* or ypbind. If this file is not available, then it can read the
* the transport address for talking to ypbind without bothering
* rpcbind. If this also fails, then it uses the old method of
* talking to rpcbind and then ypbind.
*
* We lock the first byte of the cache files after writing to them.
* This indicates to the client that they contents are valid. The
* client should test the lock. If the lock is held, then it can
* use the contents. If the lock test fails, then the contents should
* be ignored.
*/
/*
* Cache new binding information for a domain in a file. If the
* new binding is the same as the old, then we skip it. We xdr
* a 'ypbind_resp', which is what would be returned by a call to
* the YBINDPROCP_DOMAIN service. We xdr the data because it is
* easier than writing the data out field by field. It would be
* nice if there were an xdrfd_create() that was similar to
* xdrstdio_create(). Instead, we do an fdopen and use xdrstdio_create().
*/
void
{
int st;
int fd;
if (!cache_okay)
return;
/* if the domain doesn't have a cache file, then skip it */
if (pdom->cache_file == 0)
return;
/*
* If we already had a cache file for this domain, remove it. If
* a client just started accessing it, then it will either find
* it unlocked (and not use it), or continue to use it with
* old information. This is not a problem, the client will
* either fail to talk to ypserv and try to bind again, or
* will continue to use the old server.
*/
}
if (fd == -1)
return;
return;
}
xdr_destroy(&xdrs);
return;
}
/* we lock the first byte to indicate that the file is valid */
if (st == -1) {
}
}
void
{
if (!cache_okay)
return;
}
}
/*
* Cache a transport address for talking to ypbind. We convert the
* transport address to a universal address and save that in a file.
* The file lives in the binding directory because it does not depend
* on the domain.
*/
void
int vers;
{
char *uaddr;
int fd;
int st;
int len;
if (!cache_okay)
return;
if (uaddr == 0)
return;
if (fd == -1) {
return;
}
return;
}
/* we lock the first byte to indicate that the file is valid */
if (st == -1) {
}
}
/*
* Create a file that clients can check to see if we are running.
*/
void
{
int fd;
int st;
int len;
if (!cache_okay)
return;
if (fd == -1) {
return;
}
return;
}
/* we lock the first byte to indicate that the file is valid */
if (st == -1) {
}
/* we keep 'fd' open so that the lock will continue to be held */
}
/*
* We are called once at startup (when the known_domains list is empty)
* to clean up left-over files. We are also called right before
* exiting. In the latter case case we don't bother closing descriptors
* in the entries in the domain list because they will be closed
* automatically (and unlocked) when we exit.
*
* We ignore the cache_okay flag because it is important that we remove
* all cache files (left-over files can temporarily confuse clients).
*/
void
{
/* close and unlink cache files for each domain */
if (pdom->cache_file)
}
/* Directory could not be opened. */
return;
}
}
}
}
/*
* We only want to use the cache stuff on local file systems.
* For remote file systems (e.g., NFS, the locking overhead is
* worse than the overhead of loopback RPC, so the caching
* wouldn't buy us anything. In addition, if the remote locking
* software isn't configured before we start, then we would
* block when we try to lock.
*
* We don't have a direct way to tell if a file system is local
* or remote, so we assume it is local unless it is NFS.
*/
int
{
int st;
if (st == -1) {
return (0);
}
/* we use strncasecmp to get NFS, NFS3, nfs, nfs3, etc. */
return (0);
return (1);
}