336844dba4
#ifdef out a number of calls to free() left over from the original GNU ypserv implementation. As near as I can tell, the Berkeley DB package does its own garbage collection, hence the caller doesn't have to worry about free()ing the memory returned in the DBT structures during lookups (I'm still not 1005 sure about this: the DB code is very hard to follow. I must use dynamically allocated memory since you can retreive arbitrarily large records from a database, but I'm not sure where it ends up letting go of it). This was not true with GDBM; you had to do your own garbage collection. The general rule is that if you allocate memory inside an RPC service routine, you have to free() it the next time the routine is called since the underlying XDR routines won't do it for you. But if the DB package does this itself, then we don't need to do it in the main program. Note that with the original malloc(), there were never any errors flagged. phkmalloc complained quite loudly.
1385 lines
28 KiB
C
1385 lines
28 KiB
C
/*
|
|
** server.c YP server routines.
|
|
**
|
|
** Copyright (c) 1993 Signum Support AB, Sweden
|
|
**
|
|
** This file is part of the NYS YP Server.
|
|
**
|
|
** The NYS YP Server is free software; you can redistribute it and/or
|
|
** modify it under the terms of the GNU General Public License as
|
|
** published by the Free Software Foundation; either version 2 of the
|
|
** License, or (at your option) any later version.
|
|
**
|
|
** The NYS YP Server is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
** General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public
|
|
** License along with the NYS YP Server; see the file COPYING. If
|
|
** not, write to the Free Software Foundation, Inc., 675 Mass Ave,
|
|
** Cambridge, MA 02139, USA.
|
|
**
|
|
** Author: Peter Eriksson <pen@signum.se>
|
|
** Ported to FreeBSD and hacked all to pieces
|
|
** by Bill Paul <wpaul@ctr.columbia.edu>
|
|
**
|
|
** $Id: server.c,v 1.11 1995/07/15 23:27:47 wpaul Exp $
|
|
**
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <dirent.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <db.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <rpc/rpc.h>
|
|
#include "yp.h"
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <syslog.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
|
|
#if __STDC__
|
|
#include <stdarg.h>
|
|
#else
|
|
#include <varargs.h>
|
|
#endif
|
|
|
|
#define PERM_SECURE (S_IRUSR|S_IWUSR)
|
|
HASHINFO openinfo = {
|
|
4096, /* bsize */
|
|
32, /* ffactor */
|
|
256, /* nelem */
|
|
2048 * 1024, /* cachesize */
|
|
NULL, /* hash */
|
|
0, /* lorder */
|
|
};
|
|
|
|
#if TCP_WRAPPER
|
|
#include "tcpd.h"
|
|
int allow_severity=LOG_INFO;
|
|
int deny_severity=LOG_WARNING;
|
|
#endif
|
|
|
|
void verr __P((const char *, _BSD_VA_LIST_));
|
|
void Perror __P((const char *, ...));
|
|
|
|
extern char *dnsname();
|
|
extern char *dnsaddr();
|
|
extern char *_gethostbydnsaddr();
|
|
|
|
extern char *progname;
|
|
extern int errno;
|
|
|
|
int debug_flag = 0;
|
|
int dns_flag = 0;
|
|
int children = 0;
|
|
int forked = 0;
|
|
|
|
void verr(fmt, ap)
|
|
const char *fmt;
|
|
_BSD_VA_LIST_ ap;
|
|
|
|
{
|
|
if (debug_flag)
|
|
vfprintf(stderr, fmt, ap);
|
|
else
|
|
vsyslog(LOG_NOTICE, fmt, ap);
|
|
}
|
|
|
|
void
|
|
#ifdef __STDC__
|
|
Perror(const char *fmt, ...)
|
|
#else
|
|
Perror(fmt, va_list)
|
|
const char *fmt;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
va_list ap;
|
|
#ifdef __STDC__
|
|
va_start(ap, fmt);
|
|
#else
|
|
va_start(ap);
|
|
#endif
|
|
verr(fmt,ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
|
|
/*
|
|
** Return 1 if request comes from an authorized host
|
|
**
|
|
** XXX This function should implement the "securenets" functionality
|
|
*/
|
|
static int is_valid_host(struct sockaddr_in *sin)
|
|
{
|
|
#if TCP_WRAPPER
|
|
extern int hosts_ctl(char *, char *, char *, char *);
|
|
int status;
|
|
static long oldaddr=0; /* so we dont log multiple times */
|
|
static int oldstatus=-1;
|
|
char *h=NULL;
|
|
|
|
#ifdef TRYRESOLVE
|
|
struct hostent *hp;
|
|
|
|
hp = _gethostbydnsaddr((char *) &sin->sin_addr.s_addr,
|
|
sizeof (sin->sin_addr.s_addr), AF_INET);
|
|
|
|
h = (hp && hp->h_name) ? hp->h_name : NULL;
|
|
#endif
|
|
|
|
#ifndef FROM_UNKNOWN
|
|
#define FROM_UNKNOWN STRING_UNKNOWN
|
|
#endif
|
|
|
|
status = hosts_ctl(progname,
|
|
h?h:FROM_UNKNOWN,
|
|
inet_ntoa(sin->sin_addr),
|
|
"");
|
|
|
|
if (!status && (sin->sin_addr.s_addr != oldaddr || status != oldstatus)) {
|
|
syslog(status?allow_severity:deny_severity,
|
|
"%sconnect from %s\n",status?"":"refused ",
|
|
h?h:inet_ntoa(sin->sin_addr));
|
|
oldaddr=sin->sin_addr.s_addr;
|
|
oldstatus=status;
|
|
}
|
|
return status;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
void *ypproc_null_2_svc(void *dummy,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static int foo;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
if (!is_valid_host(rqhost))
|
|
return NULL;
|
|
|
|
if (debug_flag)
|
|
Perror("ypproc_null() [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
return (void *) &foo;
|
|
}
|
|
|
|
|
|
/*
|
|
** Return 1 if the name is a valid domain name served by us, else 0.
|
|
*/
|
|
static int is_valid_domain(const char *domain)
|
|
{
|
|
struct stat sbuf;
|
|
|
|
|
|
if (domain == NULL ||
|
|
strcmp(domain, "binding") == 0 ||
|
|
strcmp(domain, "..") == 0 ||
|
|
strcmp(domain, ".") == 0 ||
|
|
strchr(domain, '/'))
|
|
return 0;
|
|
|
|
if (stat(domain, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
bool_t *ypproc_domain_2_svc(domainname *name,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static bool_t result;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
Perror("ypproc_domain(\"%s\") [From: %s:%d]\n",
|
|
*name,
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (is_valid_domain(*name))
|
|
result = TRUE;
|
|
else
|
|
result = FALSE;
|
|
|
|
if (debug_flag)
|
|
Perror("\t-> %s.\n",
|
|
(result == TRUE ? "Ok" : "Not served by us"));
|
|
|
|
return &result;
|
|
}
|
|
|
|
|
|
bool_t *ypproc_domain_nonack_2_svc(domainname *name,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static bool_t result;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
Perror("ypproc_domain_nonack(\"%s\") [From: %s:%d]\n",
|
|
*name,
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (!is_valid_domain(*name))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid domain)\n");
|
|
|
|
/* Bail out and don't return any RPC value */
|
|
return NULL;
|
|
}
|
|
|
|
if (debug_flag)
|
|
Perror("\t-> OK.\n");
|
|
|
|
result = TRUE;
|
|
return &result;
|
|
}
|
|
|
|
|
|
/*
|
|
** Open a DB database
|
|
*/
|
|
static DB *open_database(const char *domain,
|
|
const char *map)
|
|
{
|
|
DB *dbp;
|
|
char buf[1025];
|
|
|
|
|
|
if (map[0] == '.' || strchr(map, '/'))
|
|
return 0;
|
|
|
|
sprintf (buf, "%s/%s", domain, map);
|
|
|
|
dbp = dbopen(buf,O_RDONLY|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
|
|
|
|
if (debug_flag > 1 && dbp == NULL)
|
|
Perror("dbopen(%s): %s", map, strerror(errno));
|
|
|
|
return dbp;
|
|
}
|
|
|
|
|
|
#define F_ALL 0x01
|
|
#define F_NEXT 0x02
|
|
#define F_YPALL 0x08
|
|
|
|
/*
|
|
** Get a record from a DB database.
|
|
** This looks ugly because it emulates the behavior of the original
|
|
** GDBM-based routines. Blech.
|
|
*/
|
|
int read_database(DB *dbp,
|
|
const DBT *ikey,
|
|
DBT *okey,
|
|
DBT *dval,
|
|
int flags)
|
|
{
|
|
int first_flag = 0;
|
|
DBT nkey, ckey, dummyval;
|
|
|
|
|
|
if (ikey == NULL || ikey->data == NULL)
|
|
{
|
|
(dbp->seq)(dbp,&ckey,&dummyval,R_FIRST);
|
|
first_flag = 1;
|
|
}
|
|
else
|
|
{
|
|
if ((flags & F_NEXT))
|
|
{
|
|
/*
|
|
** This crap would be unnecessary if R_CURSOR actually worked.
|
|
*/
|
|
if (flags < F_YPALL)
|
|
{
|
|
(dbp->seq)(dbp,&ckey,&dummyval,R_FIRST);
|
|
while(strncmp((char *)ikey->data,ckey.data,(int)ikey->size) ||
|
|
ikey->size != ckey.size)
|
|
(dbp->seq)(dbp,&ckey,&dummyval,R_NEXT);
|
|
}
|
|
if ((dbp->seq)(dbp,&ckey,&dummyval,R_NEXT))
|
|
ckey.data = NULL;
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(dummyval.data);
|
|
#endif
|
|
}
|
|
else
|
|
ckey = *ikey;
|
|
}
|
|
|
|
if (ckey.data == NULL)
|
|
{
|
|
return (flags & F_NEXT) ? YP_NOMORE : YP_NOKEY;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if ((dbp->get)(dbp,&ckey,dval,0))
|
|
{
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
/* Free key, unless it comes from the caller! */
|
|
if (ikey == NULL || ckey.data != ikey->data)
|
|
free(ckey.data);
|
|
#endif
|
|
if (ikey && ikey->data != NULL)
|
|
{
|
|
return YP_NOKEY;
|
|
}
|
|
else
|
|
if (first_flag)
|
|
return YP_BADDB;
|
|
else
|
|
return YP_FALSE;
|
|
}
|
|
|
|
if ((flags & F_ALL) || strncmp(ckey.data, "YP_", 3) != 0)
|
|
{
|
|
if (okey)
|
|
*okey = ckey;
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
else if (ikey == NULL || ikey->data != ckey.data)
|
|
free(ckey.data);
|
|
#endif
|
|
return YP_TRUE;
|
|
}
|
|
|
|
/* Free old value */
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(dval->data);
|
|
#endif
|
|
if ((dbp->seq)(dbp,&nkey,&dummyval,R_NEXT))
|
|
nkey.data = NULL;
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(dummyval.data);
|
|
|
|
/* Free old key, unless it comes from the caller! */
|
|
if (ikey == NULL || ckey.data != ikey->data)
|
|
free(ckey.data);
|
|
#endif
|
|
if (ckey.data == NULL || nkey.data == NULL)
|
|
return YP_NOMORE;
|
|
|
|
ckey = nkey;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Get the DateTimeModified value for a certain map database
|
|
*/
|
|
static unsigned long get_dtm(const char *domain,
|
|
const char *map)
|
|
{
|
|
struct stat sbuf;
|
|
char buf[1025];
|
|
|
|
|
|
strcpy(buf, domain);
|
|
strcat(buf, "/");
|
|
strcat(buf, map);
|
|
|
|
if (stat(buf, &sbuf) < 0)
|
|
return 0;
|
|
else
|
|
return (unsigned long) sbuf.st_mtime;
|
|
}
|
|
|
|
|
|
/*
|
|
** YP function "MATCH" implementation
|
|
*/
|
|
ypresp_val *ypproc_match_2_svc(ypreq_key *key,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static ypresp_val result;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
{
|
|
Perror("ypproc_match(): [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
Perror("\t\tdomainname = \"%s\"\n",
|
|
key->domain);
|
|
Perror("\t\tmapname = \"%s\"\n",
|
|
key->map);
|
|
Perror("\t\tkeydat = \"%.*s\"\n",
|
|
(int) key->key.keydat_len,
|
|
key->key.keydat_val);
|
|
}
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** If this request deals with master.passwd.* and it didn't
|
|
** originate on a privileged port (< 1024), return a YP_YPERR.
|
|
** This is our half-assed way of preventing non-root users
|
|
** on NIS clients from getting at the real password map. Bah.
|
|
*/
|
|
|
|
if (strstr(key->map, "master.passwd") != NULL &&
|
|
ntohs(rqhost->sin_port) > 1023)
|
|
{
|
|
result.stat = YP_YPERR;
|
|
return &result;
|
|
}
|
|
|
|
result.val.valdat_len = 0;
|
|
if (result.val.valdat_val)
|
|
{
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
/*
|
|
* In general, if you malloc() data in an RPC service
|
|
* routine, you have to free() it the next time that
|
|
* routine is called since the XDR routines won't free
|
|
* it for you. However, in this case, we don't have to
|
|
* do that because the DB routines do garbage collection
|
|
* for us.
|
|
*/
|
|
free(result.val.valdat_val);
|
|
#endif
|
|
result.val.valdat_val = NULL;
|
|
}
|
|
|
|
if (key->domain[0] == '\0' || key->map[0] == '\0')
|
|
result.stat = YP_BADARGS;
|
|
else if (!is_valid_domain(key->domain))
|
|
result.stat = YP_NODOM;
|
|
else
|
|
{
|
|
DBT rdat, qdat;
|
|
|
|
DB *dbp = open_database(key->domain, key->map);
|
|
if (dbp == NULL)
|
|
result.stat = YP_NOMAP;
|
|
else
|
|
{
|
|
qdat.size = key->key.keydat_len;
|
|
qdat.data = key->key.keydat_val;
|
|
|
|
result.stat = read_database(dbp, &qdat, NULL, &rdat, F_ALL);
|
|
|
|
if (result.stat == YP_TRUE)
|
|
{
|
|
result.val.valdat_len = rdat.size;
|
|
result.val.valdat_val = rdat.data;
|
|
}
|
|
|
|
(void)(dbp->close)(dbp);
|
|
}
|
|
}
|
|
|
|
if (debug_flag)
|
|
{
|
|
if (result.stat == YP_TRUE)
|
|
Perror("\t-> Value = \"%.*s\"\n",
|
|
(int) result.val.valdat_len,
|
|
result.val.valdat_val);
|
|
else
|
|
Perror("\t-> Error #%d\n", result.stat);
|
|
}
|
|
|
|
|
|
/*
|
|
** Do the jive thing if we didn't find the host in the YP map
|
|
** and we have enabled the magic DNS lookup stuff.
|
|
**
|
|
** DNS lookups are handled in a subprocess so that the server
|
|
** doesn't block while waiting for requests to complete.
|
|
*/
|
|
if (result.stat != YP_TRUE && strstr(key->map, "hosts") && dns_flag)
|
|
{
|
|
char *cp = NULL;
|
|
|
|
if (children < MAX_CHILDREN && fork())
|
|
{
|
|
children++;
|
|
return NULL;
|
|
}
|
|
else
|
|
forked++;
|
|
|
|
key->key.keydat_val[key->key.keydat_len] = '\0';
|
|
|
|
if (debug_flag)
|
|
Perror("Doing DNS lookup of %s\n", key->key.keydat_val);
|
|
|
|
if (strcmp(key->map, "hosts.byname") == 0)
|
|
cp = dnsname(key->key.keydat_val);
|
|
else if (strcmp(key->map, "hosts.byaddr") == 0)
|
|
cp = dnsaddr(key->key.keydat_val);
|
|
|
|
if (cp)
|
|
{
|
|
|
|
if (debug_flag)
|
|
Perror("\t-> OK (%s)\n", cp);
|
|
|
|
result.val.valdat_len = strlen(cp);
|
|
result.val.valdat_val = cp;
|
|
result.stat = YP_TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (debug_flag)
|
|
{
|
|
Perror("\t-> Not Found\n");
|
|
Perror("DNS lookup: %s",strerror(errno));
|
|
}
|
|
|
|
result.stat = YP_NOKEY;
|
|
}
|
|
}
|
|
|
|
return &result;
|
|
}
|
|
|
|
|
|
|
|
ypresp_key_val *ypproc_first_2_svc(ypreq_nokey *key,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static ypresp_key_val result;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
{
|
|
Perror("ypproc_first(): [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
Perror("\tdomainname = \"%s\"\n", key->domain);
|
|
Perror("\tmapname = \"%s\"\n", key->map);
|
|
#if 0
|
|
Perror("\tkeydat = \"%.*s\"\n",
|
|
(int) key->key.keydat_len,
|
|
key->key.keydat_val);
|
|
#endif
|
|
}
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** If this request deals with master.passwd.* and it didn't
|
|
** originate on a privileged port (< 1024), return a YP_YPERR.
|
|
** This is our half-assed way of preventing non-root users
|
|
** on NIS clients from getting at the real password map. Bah.
|
|
*/
|
|
|
|
if (strstr(key->map, "master.passwd") != NULL &&
|
|
ntohs(rqhost->sin_port) > 1023)
|
|
{
|
|
result.stat = YP_YPERR;
|
|
return &result;
|
|
}
|
|
|
|
result.key.keydat_len = 0;
|
|
if (result.key.keydat_val)
|
|
{
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(result.key.keydat_val);
|
|
#endif
|
|
result.key.keydat_val = NULL;
|
|
}
|
|
|
|
result.val.valdat_len = 0;
|
|
if (result.val.valdat_val)
|
|
{
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(result.val.valdat_val);
|
|
#endif
|
|
result.val.valdat_val = NULL;
|
|
}
|
|
|
|
if (key->map[0] == '\0' || key->domain[0] == '\0')
|
|
result.stat = YP_BADARGS;
|
|
else if (!is_valid_domain(key->domain))
|
|
result.stat = YP_NODOM;
|
|
else
|
|
{
|
|
DBT dkey, dval;
|
|
|
|
DB *dbp = open_database(key->domain, key->map);
|
|
if (dbp == NULL)
|
|
result.stat = YP_NOMAP;
|
|
else
|
|
{
|
|
result.stat = read_database(dbp, NULL, &dkey, &dval, 0);
|
|
|
|
if (result.stat == YP_TRUE)
|
|
{
|
|
result.key.keydat_len = dkey.size;
|
|
result.key.keydat_val = dkey.data;
|
|
|
|
result.val.valdat_len = dval.size;
|
|
result.val.valdat_val = dval.data;
|
|
}
|
|
|
|
(void)(dbp->close)(dbp);
|
|
}
|
|
}
|
|
|
|
if (debug_flag)
|
|
{
|
|
if (result.stat == YP_TRUE)
|
|
Perror("\t-> Key = \"%.*s\", Value = \"%.*s\"\n",
|
|
(int) result.key.keydat_len,
|
|
result.key.keydat_val,
|
|
(int) result.val.valdat_len,
|
|
result.val.valdat_val);
|
|
|
|
else
|
|
Perror("\t-> Error #%d\n", result.stat);
|
|
}
|
|
|
|
return &result;
|
|
}
|
|
|
|
|
|
ypresp_key_val *ypproc_next_2_svc(ypreq_key *key,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static ypresp_key_val result;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
{
|
|
Perror("ypproc_next(): [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
Perror("\tdomainname = \"%s\"\n", key->domain);
|
|
Perror("\tmapname = \"%s\"\n", key->map);
|
|
Perror("\tkeydat = \"%.*s\"\n",
|
|
(int) key->key.keydat_len,
|
|
key->key.keydat_val);
|
|
}
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** If this request deals with master.passwd.* and it didn't
|
|
** originate on a privileged port (< 1024), return a YP_YPERR.
|
|
** This is our half-assed way of preventing non-root users
|
|
** on NIS clients from getting at the real password map. Bah.
|
|
*/
|
|
|
|
if (strstr(key->map, "master.passwd") != NULL &&
|
|
ntohs(rqhost->sin_port) > 1023)
|
|
{
|
|
result.stat = YP_YPERR;
|
|
return &result;
|
|
}
|
|
|
|
result.key.keydat_len = 0;
|
|
if (result.key.keydat_val)
|
|
{
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(result.key.keydat_val);
|
|
#endif
|
|
result.key.keydat_val = NULL;
|
|
}
|
|
|
|
result.val.valdat_len = 0;
|
|
if (result.val.valdat_val)
|
|
{
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(result.val.valdat_val);
|
|
#endif
|
|
result.val.valdat_val = NULL;
|
|
}
|
|
|
|
if (key->map[0] == '\0' || key->domain[0] == '\0')
|
|
result.stat = YP_BADARGS;
|
|
else if (!is_valid_domain(key->domain))
|
|
result.stat = YP_NODOM;
|
|
else
|
|
{
|
|
DBT dkey, dval, okey;
|
|
|
|
|
|
DB *dbp = open_database(key->domain, key->map);
|
|
if (dbp == NULL)
|
|
result.stat = YP_NOMAP;
|
|
else
|
|
{
|
|
dkey.size = key->key.keydat_len;
|
|
dkey.data = key->key.keydat_val;
|
|
|
|
result.stat = read_database(dbp, &dkey, &okey, &dval, F_NEXT);
|
|
|
|
if (result.stat == YP_TRUE)
|
|
{
|
|
result.key.keydat_len = okey.size;
|
|
result.key.keydat_val = okey.data;
|
|
|
|
result.val.valdat_len = dval.size;
|
|
result.val.valdat_val = dval.data;
|
|
}
|
|
(void)(dbp->close)(dbp);
|
|
}
|
|
}
|
|
|
|
if (debug_flag)
|
|
{
|
|
if (result.stat == YP_TRUE)
|
|
Perror("\t-> Key = \"%.*s\", Value = \"%.*s\"\n",
|
|
(int) result.key.keydat_len,
|
|
result.key.keydat_val,
|
|
(int) result.val.valdat_len,
|
|
result.val.valdat_val);
|
|
else
|
|
Perror("\t-> Error #%d\n", result.stat);
|
|
}
|
|
|
|
return &result;
|
|
}
|
|
|
|
|
|
|
|
static void print_ypmap_parms(const struct ypmap_parms *pp)
|
|
{
|
|
Perror("\t\tdomain = \"%s\"\n", pp->domain);
|
|
Perror("\t\tmap = \"%s\"\n", pp->map);
|
|
Perror("\t\tordernum = %u\n", pp->ordernum);
|
|
Perror("\t\tpeer = \"%s\"\n", pp->peer);
|
|
}
|
|
|
|
|
|
/*
|
|
** Clean up after child processes signal their termination.
|
|
*/
|
|
void reapchild(sig)
|
|
int sig;
|
|
{
|
|
int st;
|
|
|
|
while (wait3(&st, WNOHANG, NULL) > 0)
|
|
children--;
|
|
}
|
|
|
|
/*
|
|
** Stole the ypxfr implementation from the yps package.
|
|
*/
|
|
ypresp_xfr *ypproc_xfr_2_svc(ypreq_xfr *xfr,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static ypresp_xfr result;
|
|
struct sockaddr_in *rqhost;
|
|
char ypxfr_command[MAXPATHLEN];
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
{
|
|
Perror("ypproc_xfr_2_svc(): [From: %s:%d]\n\tmap_parms:\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
print_ypmap_parms(&xfr->map_parms);
|
|
Perror("\t\ttransid = %u\n", xfr->transid);
|
|
Perror("\t\tprog = %u\n", xfr->prog);
|
|
Perror("\t\tport = %u\n", xfr->port);
|
|
}
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** If this request originates on a non-privileged port (< 1024),
|
|
** refuse it. We really only need to guard the master.passwd.*
|
|
** maps, but what the hell.
|
|
** This is our half-assed way of preventing non-root users
|
|
** on NIS clients from getting at the real password map. Bah.
|
|
*/
|
|
|
|
if (ntohs(rqhost->sin_port) > 1023)
|
|
{
|
|
result.xfrstat = YPXFR_REFUSED;
|
|
return &result;
|
|
}
|
|
|
|
switch(fork())
|
|
{
|
|
case 0:
|
|
{
|
|
char g[11], t[11], p[11];
|
|
|
|
sprintf (ypxfr_command, "%s/ypxfr", INSTDIR);
|
|
sprintf (t, "%u", xfr->transid);
|
|
sprintf (g, "%u", xfr->prog);
|
|
sprintf (p, "%u", xfr->port);
|
|
execl(ypxfr_command, "ypxfr", "-d", xfr->map_parms.domain, "-h",
|
|
xfr->map_parms.peer, "-f", "-C", t, g,
|
|
inet_ntoa(rqhost->sin_addr), p, xfr->map_parms.map, NULL);
|
|
Perror("ypxfr execl(): %s",strerror(errno));
|
|
exit(0);
|
|
}
|
|
case -1:
|
|
Perror("fork(): %s",strerror(errno));
|
|
result.xfrstat = YPXFR_XFRERR;
|
|
default:
|
|
{
|
|
result.xfrstat = YPXFR_SUCC;
|
|
break;
|
|
}
|
|
}
|
|
|
|
result.transid = xfr->transid;
|
|
return &result;
|
|
}
|
|
|
|
|
|
void *ypproc_clear_2_svc(void *dummy,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static int foo;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
Perror("ypproc_clear_2_svc() [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return (void *) &foo;
|
|
}
|
|
|
|
|
|
static int ypall_close(void *data)
|
|
{
|
|
DB *locptr;
|
|
|
|
if (debug_flag && data == NULL)
|
|
{
|
|
Perror("ypall_close() called with NULL pointer.\n");
|
|
return 0;
|
|
}
|
|
|
|
locptr = (DB *)data;
|
|
(void)(locptr->close)(locptr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int ypall_encode(ypresp_key_val *val,
|
|
void *data)
|
|
{
|
|
DBT dkey, dval, okey;
|
|
|
|
dkey.data = val->key.keydat_val;
|
|
dkey.size = val->key.keydat_len;
|
|
|
|
val->stat = read_database((DB *) data, &dkey, &okey, &dval, F_NEXT | F_YPALL);
|
|
|
|
if (val->stat == YP_TRUE)
|
|
{
|
|
val->key.keydat_val = okey.data;
|
|
val->key.keydat_len = okey.size;
|
|
|
|
val->val.valdat_val = dval.data;
|
|
val->val.valdat_len = dval.size;
|
|
}
|
|
|
|
|
|
return val->stat;
|
|
}
|
|
|
|
|
|
ypresp_all *ypproc_all_2_svc(ypreq_nokey *nokey,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static ypresp_all result;
|
|
extern __xdr_ypall_cb_t __xdr_ypall_cb;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
{
|
|
Perror("ypproc_all_2_svc(): [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
Perror("\t\tdomain = \"%s\"\n", nokey->domain);
|
|
Perror("\t\tmap = \"%s\"\n", nokey->map);
|
|
}
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (children < MAX_CHILDREN && fork())
|
|
{
|
|
children++;
|
|
return NULL;
|
|
}
|
|
else
|
|
forked++;
|
|
|
|
__xdr_ypall_cb.u.encode = NULL;
|
|
__xdr_ypall_cb.u.close = NULL;
|
|
__xdr_ypall_cb.data = NULL;
|
|
|
|
result.more = TRUE;
|
|
|
|
/*
|
|
** If this request deals with master.passwd.* and it didn't
|
|
** originate on a privileged port (< 1024), return a YP_YPERR.
|
|
** This is our half-assed way of preventing non-root users
|
|
** on NIS clients from getting at the real password map. Bah.
|
|
*/
|
|
|
|
if (strstr(nokey->map, "master.passwd") != NULL &&
|
|
ntohs(rqhost->sin_port) > 1023)
|
|
{
|
|
result.ypresp_all_u.val.stat = YP_YPERR;
|
|
return &result;
|
|
}
|
|
|
|
if (nokey->map[0] == '\0' || nokey->domain[0] == '\0')
|
|
result.ypresp_all_u.val.stat = YP_BADARGS;
|
|
else if (!is_valid_domain(nokey->domain))
|
|
result.ypresp_all_u.val.stat = YP_NODOM;
|
|
else
|
|
{
|
|
DBT dkey, dval;
|
|
|
|
DB *dbp = open_database(nokey->domain, nokey->map);
|
|
if (dbp == NULL)
|
|
result.ypresp_all_u.val.stat = YP_NOMAP;
|
|
else
|
|
{
|
|
result.ypresp_all_u.val.stat = read_database(dbp,
|
|
NULL,
|
|
&dkey,
|
|
&dval,
|
|
0);
|
|
|
|
if (result.ypresp_all_u.val.stat == YP_TRUE)
|
|
{
|
|
result.ypresp_all_u.val.key.keydat_len = dkey.size;
|
|
result.ypresp_all_u.val.key.keydat_val = dkey.data;
|
|
|
|
result.ypresp_all_u.val.val.valdat_len = dval.size;
|
|
result.ypresp_all_u.val.val.valdat_val = dval.data;
|
|
|
|
__xdr_ypall_cb.u.encode = ypall_encode;
|
|
__xdr_ypall_cb.u.close = ypall_close;
|
|
__xdr_ypall_cb.data = (void *) dbp;
|
|
|
|
return &result;
|
|
}
|
|
|
|
(void)(dbp->close)(dbp);
|
|
}
|
|
}
|
|
|
|
return &result;
|
|
}
|
|
|
|
|
|
ypresp_master *ypproc_master_2_svc(ypreq_nokey *nokey,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static ypresp_master result;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
{
|
|
Perror("ypproc_master_2_svc(): [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
Perror("\t\tdomain = \"%s\"\n", nokey->domain);
|
|
Perror("\t\tmap = \"%s\"\n", nokey->map);
|
|
}
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** If this request deals with master.passwd.* and it didn't
|
|
** originate on a privileged port (< 1024), return a YP_YPERR.
|
|
** This is our half-assed way of preventing non-root users
|
|
** on NIS clients from getting at the real password map. Bah.
|
|
*/
|
|
|
|
if (strstr(nokey->map, "master.passwd") != NULL &&
|
|
ntohs(rqhost->sin_port) > 1023)
|
|
{
|
|
result.stat = YP_YPERR;
|
|
return &result;
|
|
}
|
|
|
|
if (result.peer)
|
|
{
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(result.peer);
|
|
#endif
|
|
result.peer = NULL;
|
|
}
|
|
|
|
if (nokey->domain[0] == '\0')
|
|
result.stat = YP_BADARGS;
|
|
else if (!is_valid_domain(nokey->domain))
|
|
result.stat = YP_NODOM;
|
|
else
|
|
{
|
|
DB *dbp = open_database(nokey->domain, nokey->map);
|
|
if (dbp == NULL)
|
|
result.stat = YP_NOMAP;
|
|
else
|
|
{
|
|
DBT key, val;
|
|
|
|
key.size = sizeof("YP_MASTER_NAME")-1;
|
|
key.data = "YP_MASTER_NAME";
|
|
|
|
if ((dbp->get)(dbp,&key,&val,0))
|
|
{
|
|
/* No YP_MASTER_NAME record in map? Assume we are Master */
|
|
static char hostbuf[1025];
|
|
|
|
gethostname((char *)&hostbuf, sizeof(hostbuf)-1);
|
|
Perror("Hostname: [%s]",hostbuf);
|
|
result.peer = strdup(hostbuf);
|
|
}
|
|
else
|
|
{
|
|
*(((char *)val.data)+val.size) = '\0';
|
|
result.peer = val.data;
|
|
}
|
|
|
|
result.stat = YP_TRUE;
|
|
(void)(dbp->close)(dbp);
|
|
}
|
|
}
|
|
|
|
if (result.peer == NULL)
|
|
result.peer = strdup("");
|
|
|
|
if (debug_flag)
|
|
Perror("\t-> Peer = \"%s\"\n", result.peer);
|
|
|
|
return &result;
|
|
}
|
|
|
|
|
|
ypresp_order *ypproc_order_2_svc(ypreq_nokey *nokey,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static ypresp_order result;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
{
|
|
Perror("ypproc_order_2_svc(): [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
Perror("\t\tdomain = \"%s\"\n", nokey->domain);
|
|
Perror("\t\tmap = \"%s\"\n", nokey->map);
|
|
}
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** If this request deals with master.passwd.* and it didn't
|
|
** originate on a privileged port (< 1024), return a YP_YPERR.
|
|
** This is our half-assed way of preventing non-root users
|
|
** on NIS clients from getting at the real password map. Bah.
|
|
*/
|
|
|
|
if (strstr(nokey->map, "master.passwd") != NULL &&
|
|
ntohs(rqhost->sin_port) > 1023)
|
|
{
|
|
result.stat = YP_YPERR;
|
|
return &result;
|
|
}
|
|
|
|
result.ordernum = 0;
|
|
|
|
if (nokey->domain[0] == '\0')
|
|
result.stat = YP_BADARGS;
|
|
else if (!is_valid_domain(nokey->domain))
|
|
result.stat = YP_NODOM;
|
|
else
|
|
{
|
|
DB *dbp = open_database(nokey->domain, nokey->map);
|
|
if (dbp == NULL)
|
|
result.stat = YP_NOMAP;
|
|
else
|
|
{
|
|
DBT key, val;
|
|
|
|
key.size = sizeof("YP_LAST_MODIFIED")-1;
|
|
key.data = "YP_LAST_MODIFIED";
|
|
|
|
if ((dbp->get)(dbp,&key,&val,0))
|
|
{
|
|
/* No YP_LAST_MODIFIED record in map? Use DTM timestamp.. */
|
|
result.ordernum = get_dtm(nokey->domain, nokey->map);
|
|
}
|
|
else
|
|
{
|
|
result.ordernum = atoi(val.data);
|
|
#ifdef GNU_YPSERV_ARTIFACT
|
|
free(val.data);
|
|
#endif
|
|
}
|
|
|
|
result.stat = YP_TRUE;
|
|
(void)(dbp->close)(dbp);
|
|
}
|
|
}
|
|
|
|
if (debug_flag)
|
|
Perror("-> Order # %d\n", result.ordernum);
|
|
|
|
return &result;
|
|
}
|
|
|
|
|
|
static void free_maplist(ypmaplist *mlp)
|
|
{
|
|
ypmaplist *next;
|
|
|
|
while (mlp != NULL)
|
|
{
|
|
next = mlp->next;
|
|
free(mlp->map);
|
|
free(mlp);
|
|
mlp = next;
|
|
}
|
|
}
|
|
|
|
static int add_maplist(ypmaplist **mlhp,
|
|
char *map)
|
|
{
|
|
ypmaplist *mlp;
|
|
|
|
if (!strncmp(map, ".", strlen(map)) || !strncmp(map, "..", strlen(map)))
|
|
return 0;
|
|
|
|
mlp = malloc(sizeof(*mlp));
|
|
if (mlp == NULL)
|
|
return -1;
|
|
|
|
mlp->map = strdup(map);
|
|
if (mlp->map == NULL)
|
|
{
|
|
free(mlp);
|
|
return -1;
|
|
}
|
|
|
|
mlp->next = *mlhp;
|
|
*mlhp = mlp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
ypresp_maplist *ypproc_maplist_2_svc(domainname *name,
|
|
struct svc_req *rqstp)
|
|
{
|
|
static ypresp_maplist result;
|
|
struct sockaddr_in *rqhost;
|
|
|
|
|
|
rqhost = svc_getcaller(rqstp->rq_xprt);
|
|
|
|
if (debug_flag)
|
|
{
|
|
Perror("ypproc_maplist_2_svc(): [From: %s:%d]\n",
|
|
inet_ntoa(rqhost->sin_addr),
|
|
ntohs(rqhost->sin_port));
|
|
|
|
Perror("\t\tdomain = \"%s\"\n", *name);
|
|
}
|
|
|
|
if (!is_valid_host(rqhost))
|
|
{
|
|
if (debug_flag)
|
|
Perror("\t-> Ignored (not a valid source host)\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (result.maps)
|
|
free_maplist(result.maps);
|
|
|
|
result.maps = NULL;
|
|
|
|
if ((*name)[0] == '\0')
|
|
result.stat = YP_BADARGS;
|
|
else if (!is_valid_domain(*name))
|
|
result.stat = YP_NODOM;
|
|
else
|
|
{
|
|
DIR *dp;
|
|
char dirname[MAXPATHLEN];
|
|
|
|
sprintf(dirname,"./%s",*name);
|
|
dp = opendir(dirname);
|
|
if (dp == NULL)
|
|
{
|
|
if (debug_flag)
|
|
{
|
|
Perror("%s: opendir: %s", progname,strerror(errno));
|
|
}
|
|
|
|
result.stat = YP_BADDB;
|
|
}
|
|
else
|
|
{
|
|
struct dirent *dep;
|
|
|
|
while ((dep = readdir(dp)) != NULL)
|
|
if (add_maplist(&result.maps, dep->d_name) < 0)
|
|
{
|
|
result.stat = YP_YPERR;
|
|
break;
|
|
}
|
|
closedir(dp);
|
|
result.stat = YP_TRUE;
|
|
}
|
|
}
|
|
|
|
if (debug_flag)
|
|
{
|
|
if (result.stat == YP_TRUE)
|
|
{
|
|
ypmaplist *p;
|
|
|
|
p = result.maps;
|
|
Perror("-> ");
|
|
while (p->next)
|
|
{
|
|
Perror("%s,", p->map);
|
|
p = p->next;
|
|
}
|
|
putc('\n', stderr);
|
|
}
|
|
else
|
|
Perror("\t-> Error #%d\n", result.stat);
|
|
}
|
|
|
|
return &result;
|
|
}
|