8256fad9b7
chpass(1) are on the way too.) This version supports all the features of the old one and adds several new ones: - Supports real multi-domain operation (optional, can be turned on with a command-line flag). This means you can actually have several different domains all served from one NIS server and allow users in any of the supported domains to change their passwords. The old yppasswdd only allowed changing passwords in the domain that was set as the system default domain name on the NIS master server. The new one can change passwords in any domain by trying to match the user information passed to it against all the passwd maps it can find. This is something of a hack, but the yppasswd.x protocol definiton does not allow for a domain to be passwd as an argument to rpc.yppasswdd, so the server has no choice but to grope around for a likely match. Since this method can fail if the same user exists in two domains, this feature is off by default. If the feature is turned on and the server becomes confused by duplicate entries, it will abort the update. - Does not require NIS client services to be available. NIS servers do _NOT_ necessarily have to be configured as NIS clients in order to function: the ypserv, ypxfr and yppush programs I've written recently will operate fine even if the system domain name isn't set, ypbind isn't running and there are no magic '+' entries in any of the /etc files. Now rpc.yppasswdd is the same way. The old yppasswdd would not work like this because it depended on getpwent(3) and friends to look up users: this will obviously only work if the system where yppasswdd is running is configured as an NIS client. The new rpc.yppasswdd doesn't use getpwent(3) at all: instead it searches through the master.passwd map databases directly. This also makes it easier for it to handle multiple domains. - Allows the superuser on the NIS master server to change any user's password without requiring password authentication. rpc.yppasswdd creates a UNIX domain socket (/var/run/ypsock) which it monitors using the same svc_run() loop used to handle incoming RPC requests. It also clears all the permission bits for /var/run/ypsock; since this socket is owned by root, this prevents anyone except root from successfully connect()ing to it. (Using a UNIX domain socket also prevents IP spoofing attacks.) By building code into passwd(1) and chpass(1) to take advantage of this 'trusted' channel, the superuser can use them to send private requests to rpc.yppasswdd. - Allows the superuser on the NIS master to use chpass(1) to update _all_ of a user's master.passwd information. The UNIX domain access point accepts a full master.passwd style structure (along with a domain name and other information), which allows the superuser to update all of a user's master.passwd information in the NIS master.passwd maps. Normal users on NIS clients are still only allowed to change their full name and shell information with chpass. - Allows the superuser on the NIS master to _add_ records to the NIS master.passwd maps using chpass(1). This feature is also switchable with a command-line flag and is off by default.
301 lines
8.0 KiB
C
301 lines
8.0 KiB
C
/*
|
|
* Copyright (c) 1995, 1996
|
|
* Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Bill Paul.
|
|
* 4. Neither the name of the author nor the names of any co-contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $Id: yppasswd_comm.c,v 1.10 1996/02/03 04:41:59 wpaul Exp $
|
|
*/
|
|
|
|
/*
|
|
* This file contains a UNIX domain socket communications package
|
|
* that lets a client process send pseudo-RPCs to rpc.yppasswdd
|
|
* without using IP. This 'local-only' communications channel is
|
|
* only used when the superuser runs passwd(1) or chpass(1) on
|
|
* the NIS master server. The idea is that we want to grant the
|
|
* superuser permission to perfom certain special operations, but
|
|
* we need an iron-clad way to tell when we're receiving a request
|
|
* from the superuser and when we aren't. To connect to a UNIX
|
|
* domain socket, one needs to be able to access a file in the
|
|
* filesystem. The socket created by rpc.yppasswdd is owned by
|
|
* root and has all its permission bits cleared, so the only
|
|
* user who can sucessfully connect() to it is root.
|
|
*
|
|
* It is the server's responsibility to initialize the listening
|
|
* socket with the makeservsock() function and to add the socket to
|
|
* the set of file descriptors monitored by the svc_run() loop.
|
|
* Once this is done, calls made through the UNIX domain socket
|
|
* can be handled almost exactly like a normal RPC. We even use
|
|
* the XDR functions for serializing data between the client and
|
|
* server to simplify the passing of complex data structures.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/fcntl.h>
|
|
#include <rpc/rpc.h>
|
|
#include <rpcsvc/yp.h>
|
|
#include "yppasswd_comm.h"
|
|
#include "yppasswd_private.h"
|
|
#include "ypxfr_extern.h"
|
|
|
|
#ifndef lint
|
|
static const char rcsid[] = "$Id: yppasswd_comm.c,v 1.10 1996/02/03 04:41:59 wpaul Exp $";
|
|
#endif
|
|
|
|
char *sockname = "/var/run/ypsock";
|
|
FILE *serv_fp;
|
|
FILE *clnt_fp;
|
|
int serv_sock;
|
|
int clnt_sock;
|
|
|
|
/*
|
|
* serialize_data() and serialize_resp() are what really do most of
|
|
* the work. These functions (ab)use xdrstdio_create() as the interface
|
|
* to the XDR library. The RPC library uses xdrrec_create() and friends
|
|
* for TCP based connections. I suppose we could use that here, but
|
|
* the interface is a bit too complicated to justify using in an
|
|
* applicatuion such as this. With xdrstdio_create(), the only catch
|
|
* is that we need to provide a buffered file stream rather than
|
|
* a simple socket descriptor, but we can easily turn the latter into
|
|
* the former using fdopen(2).
|
|
*
|
|
* Doing things this way buys us the ability to change the form of
|
|
* the data being exchanged without having to modify any of the
|
|
* routines in this package.
|
|
*/
|
|
static int serialize_data(data, fp, op)
|
|
struct master_yppasswd *data;
|
|
FILE *fp;
|
|
int op;
|
|
{
|
|
XDR xdrs;
|
|
|
|
xdrstdio_create(&xdrs, fp, op);
|
|
|
|
if (!xdr_master_yppasswd(&xdrs, data)) {
|
|
xdr_destroy(&xdrs);
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int serialize_resp(resp, fp, op)
|
|
int *resp;
|
|
FILE *fp;
|
|
int op;
|
|
{
|
|
XDR xdrs;
|
|
|
|
xdrstdio_create(&xdrs, fp, op);
|
|
|
|
if (!xdr_int(&xdrs, resp)) {
|
|
xdr_destroy(&xdrs);
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Build the server's listening socket. The descriptor generated
|
|
* here will be monitored for new connections by the svc_run() loop.
|
|
*/
|
|
int makeservsock()
|
|
{
|
|
static int ypsock;
|
|
struct sockaddr_un us;
|
|
int len;
|
|
|
|
unlink(sockname);
|
|
|
|
if ((ypsock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
|
|
err(1, "failed to create UNIX domain socket");
|
|
|
|
bzero((char *)&us, sizeof(us));
|
|
us.sun_family = AF_UNIX;
|
|
strcpy((char *)&us.sun_path, sockname);
|
|
len = strlen(us.sun_path) + sizeof(us.sun_family) + 1;
|
|
|
|
if (bind(ypsock, (struct sockaddr *)&us, len) == -1)
|
|
err(1,"failed to bind UNIX domain socket");
|
|
|
|
listen (ypsock, 1);
|
|
|
|
return(ypsock);
|
|
}
|
|
|
|
/*
|
|
* Create a socket for a client and try to connect() it to the
|
|
* server.
|
|
*/
|
|
static int makeclntsock()
|
|
{
|
|
static int ypsock;
|
|
struct sockaddr_un us;
|
|
int len;
|
|
|
|
if ((ypsock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
|
warn("failed to create UNIX domain socket");
|
|
return(-1);
|
|
}
|
|
|
|
bzero((char *)&us, sizeof(us));
|
|
us.sun_family = AF_UNIX;
|
|
strcpy((char *)&us.sun_path, sockname);
|
|
len = strlen(us.sun_path) + sizeof(us.sun_family) + 1;
|
|
|
|
if (connect(ypsock, (struct sockaddr *)&us, len) == -1) {
|
|
warn("failed to connect to server");
|
|
return(-1);
|
|
}
|
|
|
|
return(ypsock);
|
|
}
|
|
|
|
/*
|
|
* This function is used by the server to accept a new connection
|
|
* from a client and read its request data into a master_yppasswd
|
|
* stucture.
|
|
*/
|
|
struct master_yppasswd *getdat(sock)
|
|
int sock;
|
|
{
|
|
int len;
|
|
struct sockaddr_un us;
|
|
static struct master_yppasswd pw;
|
|
struct timeval tv;
|
|
fd_set fds;
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(sock, &fds);
|
|
|
|
tv.tv_sec = CONNECTION_TIMEOUT;
|
|
tv.tv_usec = 0;
|
|
|
|
switch(select(FD_SETSIZE, &fds, NULL, NULL, &tv)) {
|
|
case 0:
|
|
yp_error("select timed out");
|
|
return(NULL);
|
|
break;
|
|
case -1:
|
|
yp_error("select() failed: %s", strerror(errno));
|
|
return(NULL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((serv_sock = accept(sock, (struct sockaddr *)&us, &len)) == -1) {
|
|
yp_error("accept failed: %s", strerror(errno));
|
|
return(NULL);
|
|
}
|
|
|
|
if ((serv_fp = fdopen(serv_sock, "r+")) == NULL) {
|
|
yp_error("fdopen failed: %s",strerror(errno));
|
|
return(NULL);
|
|
}
|
|
|
|
if (serialize_data(&pw, serv_fp, XDR_DECODE)) {
|
|
yp_error("failed to receive data");
|
|
return(NULL);
|
|
}
|
|
|
|
return(&pw);
|
|
}
|
|
|
|
/*
|
|
* Client uses this to read back a response code (a single
|
|
* integer) from the server. Note that we don't need to implement
|
|
* any special XDR function for this since an int is a base data
|
|
* type which the XDR library can handle directly.
|
|
*/
|
|
int *getresp()
|
|
{
|
|
static int resp;
|
|
|
|
if (serialize_resp(&resp, clnt_fp, XDR_DECODE)) {
|
|
warn("failed to receive response");
|
|
return(NULL);
|
|
}
|
|
|
|
fclose(clnt_fp);
|
|
close(clnt_sock);
|
|
return(&resp);
|
|
}
|
|
|
|
/*
|
|
* Create a connection to the server and send a reqest
|
|
* to be processed.
|
|
*/
|
|
int senddat(pw)
|
|
struct master_yppasswd *pw;
|
|
{
|
|
|
|
if ((clnt_sock = makeclntsock()) == -1) {
|
|
warn("failed to create socket");
|
|
return(1);
|
|
}
|
|
|
|
if ((clnt_fp = fdopen(clnt_sock, "r+")) == NULL) {
|
|
warn("fdopen failed");
|
|
return(1);
|
|
}
|
|
|
|
if (serialize_data(pw, clnt_fp, XDR_ENCODE)) {
|
|
warn("failed to send data");
|
|
return(1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* This sends a response code back to the client.
|
|
*/
|
|
int sendresp(resp)
|
|
int resp;
|
|
{
|
|
|
|
if (serialize_resp(&resp, serv_fp, XDR_ENCODE)) {
|
|
yp_error("failed to send response");
|
|
return(-1);
|
|
}
|
|
|
|
fclose(serv_fp);
|
|
close(serv_sock);
|
|
return(0);
|
|
}
|