/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:rpc.yppasswdd.c 12.0$ */
/* $ACIS:rpc.yppasswdd.c 12.0$ */
/* $Source: /ibm/acis/usr/src/nfs/etc/RCS/rpc.yppasswdd.c,v $ */

#ifndef lint
static char *rcsid = "$Header:rpc.yppasswdd.c 12.0$";
#endif

#include <sys/nfs_defines.h>

/* @(#)rpc.yppasswdd.c	1.5 87/08/13 3.2/4.3NFSSRC */
/* @(#)rpc.yppasswdd.c	1.2 86/11/20 NFSSRC */
#ifndef lint
static  char sccsid[] = "@(#)rpc.yppasswdd.c 1.1 86/09/25 Copyright 1985, 1987 Sun Microsystems, Inc.";
/*
 *
 * NFSSRC 3.2/4.3 for the VAX*
 * Copyright (C) 1987 Sun Microsystems, Inc.
 * 
 * (*)VAX is a trademark of Digital Equipment Corporation
 *
 */
#endif

/*
 * Copyright (c) 1985, 1987 by Sun Microsystems, Inc.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/file.h>
#include <rpc/rpc.h>
#include <pwd.h>
#include <rpcsvc/yppasswd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <syslog.h>

char *file;			/* file in which passwd's are found */
char	*temp	 = "/etc/ptmp";	/* lockfile for modifying 'file' */
int mflag;			/* do a make */

char *index();
int boilerplate();
extern int errno;
int Argc;
char **Argv;

main(argc, argv)
	char **argv;
{
	SVCXPRT *transp;
	int s;
	
	Argc = argc;
	Argv = argv;
	if (argc < 2) {
	    	syslog(LOG_ERR,"usage: %s file [-m arg1 arg2 ...]\n", argv[0]);
		exit(1);
	}
	file = argv[1];
	if (access(file, W_OK) < 0) {
		syslog(LOG_ERR, "can't write %s\n", file);
		exit(1);
	}
	if (argc > 2 && argv[2][0] == '-' && argv[2][1] == 'm')
		mflag++;
		
	if (chdir("/etc/yp") < 0) {
		syslog(LOG_ERR, "yppasswdd: can't chdir to /etc/yp\n");
		exit(1);
	}

	if ((s = rresvport()) < 0) {
		syslog(LOG_ERR,
		    "yppasswdd: can't bind to a privileged socket\n");
		exit(1);
	}
	transp = svcudp_create(s);
	if (transp == NULL) {
		syslog(LOG_ERR, "yppasswdd: couldn't create an RPC server\n");
		exit(1);
	}
	pmap_unset(YPPASSWDPROG, YPPASSWDVERS);
	if (!svc_register(transp, YPPASSWDPROG, YPPASSWDVERS,
	    boilerplate, IPPROTO_UDP)) {
		syslog(LOG_ERR, "yppasswdd: couldn't register yppasswdd\n");
		exit(1);
	}

	if (fork())
		exit(0);
	{ int t;
	for (t = getdtablesize()-1; t >= 0; t--)
		if (t != s)
			(void) close(t);
	}
	(void) open("/", O_RDONLY);
	(void) dup2(0, 1);
	(void) dup2(0, 2);
	{ int tt = open("/dev/tty", O_RDWR);
	  if (tt > 0) {
		ioctl(tt, TIOCNOTTY, 0);
		close(tt);
	  }
	}

	svc_run();
	syslog(LOG_ALERT, "yppasswdd: svc_run shouldn't have returned\n");
}

boilerplate(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	switch(rqstp->rq_proc) {
		case NULLPROC:
			if (!svc_sendreply(transp, xdr_void, 0))
				syslog(LOG_ERR,
				    "yppasswdd: couldn't reply to RPC call\n");
			break;
		case YPPASSWDPROC_UPDATE:
			changepasswd(rqstp, transp);
			break;
	}
}

changepasswd(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	int tempfd, i, len;
	static int ans;
	FILE *tempfp, *filefp, *fp;
	char buf[256], *p;
	char cmdbuf[BUFSIZ];
	int (*f1)(), (*f2)(), (*f3)();
	struct passwd *oldpw, *newpw;
	struct yppasswd yppasswd;
	union wait status;
	char tempname[BUFSIZ];
	
	bzero(&yppasswd, sizeof(yppasswd));
	if (!svc_getargs(transp, xdr_yppasswd, &yppasswd)) {
		svcerr_decode(transp);
		return;
	}
	/* 
	 * Clean up from previous changepasswd() call
	 */
	while (wait3(&status, WNOHANG, 0) > 0)
		continue;

	newpw = &yppasswd.newpw;
	ans = 1;
	oldpw = getpwnam(newpw->pw_name);
	if (oldpw == NULL) {
		syslog(LOG_ERR, "yppasswdd: no passwd for %s\n",
		    newpw->pw_name);
		goto done;
	}
	if (oldpw->pw_passwd && *oldpw->pw_passwd &&
	    strcmp(crypt(yppasswd.oldpass,oldpw->pw_passwd),
	    oldpw->pw_passwd) != 0) {
		syslog(LOG_ERR, "yppasswdd: %s: bad passwd\n", newpw->pw_name);
		goto done;
	}
	/* (void) umask(0); */
	(void) umask(2);
	
	f1 = signal(SIGHUP, SIG_IGN);
	f2 = signal(SIGINT, SIG_IGN);
	f3 = signal(SIGQUIT, SIG_IGN);
	for (i = 0; i < Argc; ++i) {
		if (!strncmp("DIR=", Argv[i], strlen("DIR="))) {
			strcpy(tempname, Argv[i]+strlen("DIR="));
			strcat(tempname, "/ptmp");
			temp = tempname;
		}
	}
	tempfd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0644);
	if (tempfd < 0) {
		syslog(LOG_ERR, "yppasswdd: ");
		if (errno == EEXIST)
			syslog(LOG_ERR, "password file busy - try again.\n");
		else
			syslog(LOG_ERR,temp);
		goto cleanup;
	}
	signal(SIGTSTP, SIG_IGN);
	if ((tempfp = fdopen(tempfd, "w")) == NULL) {
		syslog(LOG_ERR, "yppasswdd: fdopen failed?\n");
		goto cleanup;
	}
	/*
	 * Copy passwd to temp, replacing matching lines
	 * with new password.
	 */
	if ((filefp = fopen(file, "r")) == NULL) {
		syslog(LOG_ERR, "yppasswdd: fopen of %s failed?\n", file);
		goto cleanup;
	}
	len = strlen(newpw->pw_name);
	while (fgets(buf, sizeof(buf), filefp)) {
		p = index(buf, ':');
		if (p && p - buf == len
		    && strncmp(newpw->pw_name, buf, p - buf) == 0) {
			fprintf(tempfp,"%s:%s:%d:%d:%s:%s:%s\n",
			    oldpw->pw_name,
			    newpw->pw_passwd,
			    oldpw->pw_uid,
			    oldpw->pw_gid,
			    oldpw->pw_gecos,
			    oldpw->pw_dir,
			    oldpw->pw_shell);
		}
		else
			fputs(buf, tempfp);
	}
	fclose(filefp);
	fclose(tempfp);
	if (rename(temp, file) < 0) {
		syslog(LOG_ERR, "yppasswdd: "); syslog(LOG_ERR,"rename: %m");
		unlink(temp);
		goto cleanup;
	}
	ans = 0;
	if (mflag && fork() == 0) {
		strcpy(cmdbuf, "make");
		for (i = 3; i < Argc; i++) {
			strcat(cmdbuf, " ");
			strcat(cmdbuf, Argv[i]);
		}
#ifdef DEBUG
		fprintf(stderr, "about to execute %s\n", cmdbuf);
#else
		system(cmdbuf);
#endif
		exit(0);
	}
    cleanup:
	fclose(tempfp);
	signal(SIGHUP, f1);
	signal(SIGINT, f2);
	signal(SIGQUIT, f3);
    done:
	if (!svc_sendreply(transp, xdr_int, &ans))
		syslog(LOG_ERR, "yppasswdd: couldnt reply to RPC call\n");
}

rresvport()
{
	struct sockaddr_in sin;
	int s, alport = IPPORT_RESERVED - 1;

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = 0;
	s = socket(AF_INET, SOCK_DGRAM, 0, 0);
	if (s < 0)
		return (-1);
	for (;;) {
		sin.sin_port = htons((u_short)alport);
		if (bind(s, (caddr_t)&sin, sizeof (sin), 0) >= 0)
			return (s);
		if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
			syslog(LOG_ERR,"socket: %m");
			return (-1);
		}
		(alport)--;
		if (alport == IPPORT_RESERVED/2) {
			syslog(LOG_ERR, "socket: All ports in use\n");
			return (-1);
		}
	}
}

static char *
pwskip(p)
register char *p;
{
	while( *p && *p != ':' && *p != '\n' )
		++p;
	if( *p ) *p++ = 0;
	return(p);
}

struct passwd *
getpwnam(name)
	char *name;
{
	FILE *pwf;
	int cnt;
	char *p, *pt;
	static char line[BUFSIZ+1];
	static struct passwd passwd;
	
	pwf = fopen(file, "r");
	if (pwf == NULL)
		return (NULL);
	cnt = strlen(name);
	while (p = fgets(line, BUFSIZ, pwf)) {
		pt = index(line, ':');
		if (pt && pt - line == cnt
	    	    && strncmp(name, line, pt - line) == 0) {
			passwd.pw_name = p;
			p = pwskip(p);
			passwd.pw_passwd = p;
			p = pwskip(p);
			passwd.pw_uid = atoi(p);
			p = pwskip(p);
			passwd.pw_gid = atoi(p);
			passwd.pw_quota = 0;
			passwd.pw_comment = "";
			p = pwskip(p);
			passwd.pw_gecos = p;
			p = pwskip(p);
			passwd.pw_dir = p;
			p = pwskip(p);
			passwd.pw_shell = p;
			while(*p && *p != '\n') p++;
			*p = '\0';
			fclose(pwf);
			return (&passwd);
		}
	}
	fclose(pwf);
	return (NULL);
}

/* 
 * NFSSRC 3.2/4.3 for the VAX*
 * Copyright (C) 1987 Sun Microsystems, Inc.
 * 
 * (*)VAX is a trademark of Digital Equipment Corporation
 */
