view usr/src/cmd/rpcsvc/nis/rpc.nispasswdd/rpc.nispasswdd.c @ 10008:b5d6e292a984

PSARC 2008/295 NISPASSWD_VERS2 6563443 nisaddcred and chkey have issues in md5 password encyption with passwords > eight characters
author Ashok Kumar T <Ashok.Kumar@Sun.COM>
date Wed, 01 Jul 2009 22:27:19 +0530
parents 861de40d3b19
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>	/* getenv, exit */
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>
#include <syslog.h>
#include <stropts.h>
#include <netdir.h>
#include <synch.h>
#include <rpc/rpc.h>
#include <rpcsvc/nis.h>
#include <rpcsvc/yppasswd.h>
#include <rpcsvc/nispasswd.h>
#include <rpcsvc/nis_dhext.h>
#include "npd_svcsubr.h"

int	verbose = 0;
bool_t	debug = FALSE;
/*
 * generate keys if none exist or if admin. is changing another
 * users password.
 */
bool_t	generatekeys = FALSE;
ulong_t	cache_time = 1800;	/* cache failed attempts for 30 mins */
int	max_attempts = 3;	/* max # of failed attempts allowed */
NIS_HASH_TABLE	upd_list;	/* cache of updates */
char	*ypfwd = (char *)NULL;	/* passwd map YP master machine */

static void __msgout(const char *msg);
static int __svc_priv_port_create(void);
static void nispasswd_prog(struct svc_req *rqstp, register SVCXPRT *transp);
static void nispasswd_prog_ver2(struct svc_req *rqstp,
				    register SVCXPRT *transp);
static void yppasswd_prog(struct svc_req *rqstp, register SVCXPRT *transp);
static void set_rpc_gss_svc_names(void);

int
main(int argc, char *argv[])
{
	pid_t	pid;
	int	i;
	int	c;
	int	mins;
	int	status;
	nis_tag		tags[2], *tagres;
	nis_server	*srv;
	int	connmaxrec = RPC_MAXDATASIZE;

	(void) memset((char *)&upd_list, 0, sizeof (upd_list));
	(void) chdir("/var/nis");	/* drop core here */
	while ((c = getopt(argc, argv, "a:c:DgvY:")) != -1) {
	switch (c) {
		case 'a':
			max_attempts = atoi(optarg);
			if (max_attempts < 0) {
				fprintf(stderr,
				"%s: invalid number of maximum attempts\n",
				    argv[0]);
				exit(1);
			}
			break;
		case 'c':
			mins = atoi(optarg);
			if (mins <= 0) {
				fprintf(stderr,
				    "%s: invalid cache time\n", argv[0]);
				exit(1);
			}
			cache_time = (ulong_t)mins * 60;
			break;
		case 'D':	/* debug mode */
			debug = TRUE;
			break;
		case 'g':	/* generate new keys if none exist */
			generatekeys = TRUE;
			break;
		case 'v':	/* verbose mode */
			verbose++;
			break;
		case 'Y':	/* YP forward mode */
			if (!(ypfwd = strdup(optarg))) {
				fprintf(stderr, "%s: out of memory\n", argv[0]);
				exit(1);
			}
			break;
		case '?':
			fprintf(stderr,
			"Usage: %s [-a attempts] [-c minutes] [-D] [-g] [-v]\n",
			    argv[0]);
			exit(1);
		}
	}

	/*
	 * this check should be removed if 'root' is not the
	 * owner of the table or if 'root' is not a member of
	 * the nis+ admins. group.
	 * of course: you also don't want someone to spoof the
	 * the daemon.
	 */
	if (geteuid() != (uid_t)0) {
		char err_string[256];
		snprintf(err_string, sizeof (err_string),
		    "must be superuser to run %s", argv[0]);
		__msgout(err_string);
		exit(1);
	}

	if (debug == FALSE) {
		pid = fork();
		if (pid < 0) {
			perror("cannot fork");
			exit(1);
		}
		if (pid)
			exit(0);
		closefrom(0);
		i = open("/dev/console", 2);
		(void) dup2(i, 1);
		(void) dup2(i, 2);
		(void) setsid();
	}
	openlog("rpc.nispasswdd", LOG_PID, LOG_DAEMON);

	/*
	 * should I be running ?
	 * Get the list of directories the local NIS+ server serves and
	 * check if it is the master server of any 'org_dir' listed.
	 */
	srv = __nis_host2nis_server(NULL, 0, &status);
	if (srv == NULL) {
		if (debug == TRUE)
			(void) fprintf(stderr,
			"no host/address information for local host, %d\n",
			    status);
		else
			syslog(LOG_ERR,
			"no host/address information for local host, %d",
			    status);
		__msgout("exiting ...");
		exit(1);
	}
	tags[0].tag_type = TAG_DIRLIST;
	tags[0].tag_val = "";
	tags[1].tag_type = TAG_NISCOMPAT;
	tags[1].tag_val = "";

	status = nis_stats(srv, tags, 2, &tagres);
	__free_nis_server(srv);
	if (status != NIS_SUCCESS) {
		if (verbose)
			nis_perror(status, "rpc.nispasswdd");
		else
			nis_lerror(status, "rpc.nispasswdd");
		__msgout(" ... exiting ...");
		exit(1);
	}
	if ((strcmp(tagres[0].tag_val, "<Unknown Statistics>") == 0) ||
	    (strcmp(tagres[1].tag_val, "<Unknown Statistics>") == 0)) {

		/* old server */
	__msgout("NIS+ server does not support the new statistics tags");
		exit(1);
	}

	if (__npd_am_master(nis_local_host(), tagres[0].tag_val) == FALSE) {
		__msgout("Local NIS+ server is not a master server");
		__msgout(" ... exiting ...");
		exit(1);
	}

	/* Specify maximum connection oriented RPC record size */
	if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
		__msgout("unable to set maximum RPC record size");
	}

	/*
	 * Check if NIS+ server is running in NIS compat mode.
	 * Register YPD only if this is the case.
	 */
	if (strcasecmp(tagres[1].tag_val, "ON") == 0) {
		if (rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, 0) == FALSE) {
		__msgout("unable to de-register (YPPASSWDPROG, YPPASSWDVERS)");
			__msgout(" ... exiting ...");
			exit(1);
		}
		if (__svc_priv_port_create() == FALSE) {
		__msgout("unable to create (YPPASSWDPROG, YPPASSWDVERS)");
			/* continue */
		}
	}
	nis_freetags(tagres, 2);

	set_rpc_gss_svc_names();

	if (rpcb_unset(NISPASSWD_PROG, NISPASSWD_VERS, 0) == FALSE) {
	__msgout("unable to de-register (NISPASSWD_PROG, NISPASSWD_VERS)");
		__msgout(" ... exiting ...");
		exit(1);
	}
	if (svc_create(nispasswd_prog, NISPASSWD_PROG, NISPASSWD_VERS,
	    "circuit_v") == 0) {
		__msgout("unable to create (NISPASSWD_PROG, NISPASSWD_VERS)");
		__msgout(" ... exiting ...");
		exit(1);
	}

	if (rpcb_unset(NISPASSWD_PROG, NISPASSWD_VERS2, 0) == FALSE) {
	__msgout("unable to de-register (NISPASSWD_PROG, NISPASSWD_VERS2)");
		__msgout(" ... exiting ...");
		exit(1);
	}
	if (svc_create(nispasswd_prog_ver2, NISPASSWD_PROG, NISPASSWD_VERS2,
	    "circuit_v") == 0) {
		__msgout("unable to create (NISPASSWD_PROG, NISPASSWD_VERS2)");
		__msgout(" ... exiting ...");
		exit(1);
	}

	if (verbose == TRUE)
		__msgout("starting rpc.nispasswdd ...");
	svc_run();
	__msgout("svc_run returned");

	return (1);
}

static void
__msgout(const char *msg)
{
	if (debug == TRUE)
		(void) fprintf(stderr, "%s\n", msg);
	else
		syslog(LOG_ERR, msg);
}

static bool_t
__svc_priv_port_create()
{
	struct netconfig *nconf, *nconf4 = 0, *nconf6 = 0;
	void		*handlep;
	int		fd4 = -1, fd6 = -1;
	struct t_info	tinfo4, tinfo6;
	SVCXPRT		*transp4 = 0, *transp6 = 0;
	int		svc4 = 0, svc6 = 0;

	if ((handlep = setnetconfig()) == (void *) NULL) {
		(void) nc_perror("cannot get any transport information");
		return (FALSE);
	}

	while ((nconf = getnetconfig(handlep)) != 0 &&
	    (nconf4 == 0 || nconf6 == 0)) {
		if ((nconf->nc_semantics == NC_TPI_CLTS) &&
		    strcmp(nconf->nc_proto, NC_UDP) == 0) {
			if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
				nconf4 = nconf;
			else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
				nconf6 = nconf;
		}
	}

	if (nconf4 == 0 && nconf6 == 0) {
		(void) endnetconfig(handlep);
		__msgout("no NC_INET/NC_INET6 NC_TPI_CLTS/NC_UPD transports");
		return (FALSE);
	}

	if (nconf4 != 0)
		fd4 = t_open(nconf4->nc_device, O_RDWR, &tinfo4);
	if (nconf6 != 0)
		fd6 = t_open(nconf6->nc_device, O_RDWR, &tinfo6);
	if (fd4 == -1 && fd6 == -1) {
		__msgout("unable to open connection for NIS requests");
		(void) endnetconfig(handlep);
		return (FALSE);
	}

	if (fd4 != -1) {
		if (netdir_options(nconf4, ND_SET_RESERVEDPORT, fd4, 0)	== -1) {
			__msgout("unable to get reserved port for NC_INET");
			netdir_perror("");
			(void) endnetconfig(handlep);
			return (FALSE);
		}
		transp4 = svc_tli_create(fd4, nconf4, NULL, 0, 0);
	}

	if (fd6 != -1) {
		if (netdir_options(nconf6, ND_SET_RESERVEDPORT, fd6, 0)	== -1) {
			__msgout("unable to get reserved port for NC_INET6");
			netdir_perror("");
			(void) endnetconfig(handlep);
			return (FALSE);
		}
		transp6 = svc_tli_create(fd6, nconf6, NULL, 0, 0);
	}

	if (transp4 == 0 && transp6 == 0) {
		__msgout("unable to create (YPPASSWDPROG, YPPASSWDVERS)");
		(void) endnetconfig(handlep);
		return (FALSE);
	}

	if (transp4 != 0)
		svc4 = svc_reg(transp4, YPPASSWDPROG, YPPASSWDVERS,
		    yppasswd_prog, nconf4);
	if (transp6 != 0)
		svc6 = svc_reg(transp6, YPPASSWDPROG, YPPASSWDVERS,
		    yppasswd_prog, nconf6);
	if (svc4 == 0 && svc6 == 0) {
		__msgout("unable to register (YPPASSWDPROG, YPPASSWDVERS)");
		if (transp4 != 0)
			(void) svc_destroy(transp4);
		if (transp6 != 0)
			(void) svc_destroy(transp6);
		(void) endnetconfig(handlep);
		return (FALSE);
	}
	(void) endnetconfig(handlep);
	return (TRUE);
}

static void
nispasswd_prog(rqstp, transp)
struct svc_req	*rqstp;
register SVCXPRT *transp;
{
	union {
		npd_request nispasswd_authenticate_1_arg;
		npd_update nispasswd_update_1_arg;
	} argument;
	union {
		nispasswd_authresult nispasswd_authenticate_1_res;
		nispasswd_updresult nispasswd_update_1_res;
	} result;
	bool_t retval;
	xdrproc_t xdr_argument, xdr_result;
	bool_t (*local)(char *, void *, struct svc_req *);

	switch (rqstp->rq_proc) {
	case NULLPROC:
		__msgout("received NIS+ null proc call");
		(void) svc_sendreply(transp,
			(xdrproc_t)xdr_void, (char *)NULL);
		return;

	case NISPASSWD_AUTHENTICATE:
		xdr_argument = (xdrproc_t)xdr_npd_request;
		xdr_result = (xdrproc_t)xdr_nispasswd_authresult;
		local = (bool_t (*) (char *, void *, struct svc_req *))
				nispasswd_authenticate_1_svc;
		break;

	case NISPASSWD_UPDATE:
		xdr_argument = (xdrproc_t)xdr_npd_update;
		xdr_result = (xdrproc_t)xdr_nispasswd_updresult;
		local = (bool_t (*) (char *, void *, struct svc_req *))
				nispasswd_update_1_svc;
		break;

	default:
		svcerr_noproc(transp);
		return;
	}
	(void) memset((char *)&argument, 0, sizeof (argument));
	if (svc_getargs(transp, xdr_argument, (caddr_t)&argument) == 0) {
		svcerr_decode(transp);
		return;
	}
	(void) memset((char *)&result, 0, sizeof (result));
	retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp);
	if (retval == TRUE && (svc_sendreply(transp, xdr_result,
						(char *)&result) == 0)) {
		svcerr_systemerr(transp);
	}
	if (svc_freeargs(transp, xdr_argument, (caddr_t)&argument) == 0) {
		__msgout("unable to free arguments");
		exit(1);
	}
	if (nispasswd_prog_1_freeresult(transp, xdr_result,
				(caddr_t)&result) == 0)
		__msgout("unable to free results");

	/* NOTREACHED */
}

static void
nispasswd_prog_ver2(rqstp, transp)
struct svc_req	*rqstp;
register SVCXPRT *transp;
{

	union {
		npd_request nispasswd_authenticate_2_arg;
		npd_update2 nispasswd_update_2_arg;
	} argument;
	union {
		nispasswd_authresult nispasswd_authenticate_2_res;
		nispasswd_updresult nispasswd_update_2_res;
	} result;
	bool_t retval;
	xdrproc_t xdr_argument, xdr_result;
	bool_t (*local)(char *, void *, struct svc_req *);

	switch (rqstp->rq_proc) {
	case NULLPROC:
		__msgout("received NIS+ null proc call");
		(void) svc_sendreply(transp,
			(xdrproc_t)xdr_void, (char *)NULL);
		return;

	case NISPASSWD_AUTHENTICATE:
		xdr_argument = (xdrproc_t)xdr_npd_request;
		xdr_result = (xdrproc_t)xdr_nispasswd_authresult;
		local = (bool_t (*) (char *, void *, struct svc_req *))
				nispasswd_authenticate_2_svc;
		break;

	case NISPASSWD_UPDATE:
		xdr_argument = (xdrproc_t)xdr_npd_update2;
		xdr_result = (xdrproc_t)xdr_nispasswd_updresult;
		local = (bool_t (*) (char *, void *, struct svc_req *))
				nispasswd_update_2_svc;
		break;

	default:
		svcerr_noproc(transp);
		return;
	}
	(void) memset((char *)&argument, 0, sizeof (argument));
	if (svc_getargs(transp, xdr_argument, (caddr_t)&argument) == 0) {
		svcerr_decode(transp);
		return;
	}
	(void) memset((char *)&result, 0, sizeof (result));
	retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp);
	if (retval == TRUE && (svc_sendreply(transp, xdr_result,
						(char *)&result) == 0)) {
		svcerr_systemerr(transp);
	}
	if (svc_freeargs(transp, xdr_argument, (caddr_t)&argument) == 0) {
		__msgout("unable to free arguments");
		exit(1);
	}
	if (nispasswd_prog_2_freeresult(transp, xdr_result,
				(caddr_t)&result) == 0)
		__msgout("unable to free results");

	/* NOTREACHED */
}

static void
yppasswd_prog(rqstp, transp)
struct svc_req	*rqstp;
register SVCXPRT *transp;
{
	union {
		struct yppasswd yppasswdproc_update_1_arg;
	} argument;
	union {
		int yppasswdproc_update_1_res;
	} result;
	bool_t retval;
	xdrproc_t xdr_argument, xdr_result;
	bool_t (*local)(char *, void *, struct svc_req *);
#ifdef SHD_WE_ALWAYS
	struct netconfig	*nconf;
#endif

	switch (rqstp->rq_proc) {
	case NULLPROC:
		__msgout("received NIS null proc call");
		(void) svc_sendreply(transp,
			(xdrproc_t)xdr_void, (char *)NULL);
		return;

	case YPPASSWDPROC_UPDATE:
		xdr_argument = (xdrproc_t)xdr_yppasswd;
		xdr_result = (xdrproc_t)xdr_int;
		local = (bool_t (*) (char *, void *, struct svc_req *))
				yppasswdproc_update_1_svc;
		break;

	default:
		svcerr_noproc(transp);
		return;
	}
#ifdef SHD_WE_ALWAYS
	/*
	 * update request received, lets just check the port
	 */
	nconf = (struct netconfig *)getnetconfigent(transp->xp_netid);
	if (nconf == (struct netconfig *)NULL) {
		(void) nc_perror("could not get transport information");
		svcerr_systemerr(transp);
		return;
	}

	if ((strcmp(nconf->nc_protofmly, NC_INET) != 0) ||
		(strcmp(nconf->nc_proto, NC_UDP) != 0) ||
		(netdir_options(nconf, ND_CHECK_RESERVEDPORT, transp->xp_fd,
			(void *) &(transp->xp_rtaddr)) != 0)) {

		if (nconf)
			(void) freenetconfigent(nconf);
		svcerr_weakauth(transp);
		return;
	}
	if (nconf)
		(void) freenetconfigent(nconf);
#endif
	(void) memset((char *)&argument, 0, sizeof (argument));
	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
		svcerr_decode(transp);
		return;
	}
	(void) memset((char *)&result, 0, sizeof (result));
	retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp);
	if (retval == TRUE && !svc_sendreply(transp, xdr_result,
						(char *)&result)) {
		svcerr_systemerr(transp);
	}
	if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
		__msgout("unable to free arguments");
		exit(1);
	}
}

/*
 * Loop thru mech list from security conf file and set
 * the RPC GSS service name(s).  Stop processing list if
 * the classic AUTH_DES compat entry is encountered.
 */
static void
set_rpc_gss_svc_names()
{
	mechanism_t **mechs;

	if (mechs = __nis_get_mechanisms(FALSE)) {
		mechanism_t	**mpp;
				/* "nispasswd" + 1 = 10 */
		char		svc_name[NIS_MAXNAMELEN+10];
		int		slen;
		char		*lh = nis_local_host();

		if (! lh) {
			syslog(LOG_ERR,
		"can't set RPC GSS service name:  can't get local host name");
			__nis_release_mechanisms(mechs);
			return;
		}

		/* '@' + NUL = 2 */
		if (strlen(lh) + strlen(NIS_SVCNAME_NISPASSWD) + 2 >
		    sizeof (svc_name)) {
			syslog(LOG_ERR,
		"can't set RPC GSS service name:  svc_name bufsize too small");
			__nis_release_mechanisms(mechs);
			return;
		}
		/* service names are of the form svc@server.dom */
		(void) sprintf(svc_name, "%s@%s", NIS_SVCNAME_NISPASSWD, lh);
		/* remove trailing '.' */
		slen = strlen(svc_name);
		if (svc_name[slen - 1] == '.')
			svc_name[slen - 1] = '\0';

		for (mpp = mechs; *mpp; mpp++) {
			mechanism_t *mp = *mpp;
			bool_t	val;

			if (AUTH_DES_COMPAT_CHK(mp))
				break;

			if (! VALID_MECH_ENTRY(mp))
				continue;

			val = rpc_gss_set_svc_name(svc_name,  mp->mechname,
			    0, NISPASSWD_PROG,
			    NISPASSWD_VERS);
			if (val) {
				if (verbose)
					syslog(LOG_INFO,
				"RPC GSS service name for mechanism '%s' set",
					    mp->mechname);
			} else {
				rpc_gss_error_t	err;

				rpc_gss_get_error(&err);
				syslog(LOG_ERR,
				    "can't set RPC GSS svc name '%s'"
				    "for mech '%s': RPC GSS err = %d,"
				    "sys err = %d",
				    svc_name, mp->mechname,
				    err.rpc_gss_error, err.system_error);
			}

			val = rpc_gss_set_svc_name(svc_name,  mp->mechname,
			    0, NISPASSWD_PROG,
			    NISPASSWD_VERS2);
			if (val) {
				if (verbose)
					syslog(LOG_INFO,
				"RPC GSS service name for mechanism '%s' set",
					    mp->mechname);
			} else {
				rpc_gss_error_t	err;

				rpc_gss_get_error(&err);
				syslog(LOG_ERR,
				    "can't set RPC GSS svc name '%s'"
				    "for mech '%s': RPC GSS err = %d,"
				    "sys err = %d",
				    svc_name, mp->mechname,
				    err.rpc_gss_error, err.system_error);
			}
		}
		__nis_release_mechanisms(mechs);
		return;
	}
}