view usr/src/lib/nsswitch/nis/common/getexecattr.c @ 3176:a38e922f22b0

6495346 memory leak in nss_dns.so.1/_nss_dns_gethost_withttl 6495347 getexecuser/getexecprof leak memory in nss_nis.so and nss_nisplus.so
author michen
date Mon, 27 Nov 2006 13:17:04 -0800
parents 5228d1267a01
children 7a2f3e83a236
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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <exec_attr.h>
#include <rpcsvc/ypclnt.h>
#include <rpcsvc/yp_prot.h>
#include "nis_common.h"


/* extern from nis_common.c */
extern void massage_netdb(const char **, int *);
/* externs from libnsl */
extern int _doexeclist(nss_XbyY_args_t *);
extern char *_exec_wild_id(char *, const char *);
extern void _exec_cleanup(nss_status_t, nss_XbyY_args_t *);
extern char *_strtok_escape(char *, char *, char **);

typedef struct __exec_nis_args {
	int		*yp_status;
	nss_XbyY_args_t	*argp;
} _exec_nis_args;


/*
 * check_match: returns 1 if -  matching entry found and no more entries needed,
 *				or, entry cannot be found because of error;
 *		returns 0 if -  no matching entry found, or,
 *				matching entry found and next match needed.
 */
static int
check_match(nss_XbyY_args_t *argp, int check_policy)
{
	execstr_t	*exec = (execstr_t *)(argp->returnval);
	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
	const char	*name = _priv_exec->name;
	const char	*type = _priv_exec->type;
	const char	*id = _priv_exec->id;
	const char	*policy = _priv_exec->policy;

	if (name && id) {
		/*
		 * NSS_DBOP_EXECATTR_BYNAMEID searched for name and id in
		 * _exec_nis_lookup already.
		 * If we're talking to pre-Solaris9 nis servers, check policy,
		 * as policy was not a searchable column then.
		 */
		if ((check_policy && policy &&
		    (strcmp(policy, exec->policy) != 0)) ||
		    (type && (strcmp(type, exec->type) != 0))) {
			return (0);
		}
	} else if ((policy && exec->policy &&
	    (strcmp(policy, exec->policy) != 0)) ||
	    (name && exec->name && (strcmp(name, exec->name) != 0)) ||
	    (type && exec->type && (strcmp(type, exec->type) != 0)) ||
	    (id && exec->id && (strcmp(id, exec->id) != 0))) {
		return (0);
	}

	return (1);
}

/*
 * check_match_strbuf: set up the data needed by check_match()
 * and call it to match exec_attr data in strbuf and argp->key.attrp
 */
static int
check_match_strbuf(nss_XbyY_args_t *argp, char *strbuf, int check_policy)
{
	char		*last = NULL;
	char		*sep = KV_TOKEN_DELIMIT;
	execstr_t	exec;
	execstr_t	*execp = &exec;
	void		*sp;
	int		rc;

	/*
	 * Remove newline that yp_match puts at the
	 * end of the entry it retrieves from the map.
	 */
	if (strbuf[argp->returnlen] == '\n') {
		strbuf[argp->returnlen] = '\0';
	}

	execp->name = _strtok_escape(strbuf, sep, &last);
	execp->policy = _strtok_escape(NULL, sep, &last);
	execp->type = _strtok_escape(NULL, sep, &last);
	execp->res1 = _strtok_escape(NULL, sep, &last);
	execp->res2 = _strtok_escape(NULL, sep, &last);
	execp->id = _strtok_escape(NULL, sep, &last);

	sp = argp->returnval;
	argp->returnval = execp;
	rc = check_match(argp, check_policy);
	argp->returnval = sp;
	free(strbuf);

	return (rc);
}

static  nss_status_t
_exec_nis_parse(const char *instr,
    int instr_len,
    nss_XbyY_args_t *argp,
    int check_policy)
{
	int		parse_stat;
	nss_status_t	res;
	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
	char		*strbuf;
	int		check_matched;

	argp->returnval = NULL;
	argp->returnlen = 0;
	parse_stat = (*argp->str2ent)(instr, instr_len, argp->buf.result,
	    argp->buf.buffer, argp->buf.buflen);
	switch (parse_stat) {
	case NSS_STR_PARSE_SUCCESS:
		argp->returnlen = instr_len;
		/* if exec_attr file format requested */
		if (argp->buf.result == NULL) {
			argp->returnval = argp->buf.buffer;
			if ((strbuf = strdup(instr)) == NULL)
				res = NSS_UNAVAIL;
			check_matched = check_match_strbuf(argp,
				strbuf, check_policy);
		} else {
			argp->returnval = argp->buf.result;
			check_matched = check_match(argp, check_policy);
		}
		if (check_matched) {
			res = NSS_SUCCESS;
			if (_priv_exec->search_flag == GET_ALL) {
				if (_doexeclist(argp) == 0) {
					res = NSS_UNAVAIL;
				}
			}
		} else {
			res = NSS_NOTFOUND;
		}
		break;
	case NSS_STR_PARSE_ERANGE:
		argp->erange = 1;
		res = NSS_NOTFOUND;
		break;
	default:
		res = NSS_UNAVAIL;
		break;
	}

	return (res);
}

/*
 * This is the callback for yp_all. It returns 0 to indicate that it wants to
 * be called again for further key-value pairs, or returns non-zero to stop the
 * flow of key-value pairs. If it returns a non-zero value, it is not called
 * again. The functional value of yp_all is then 0.
 */
/*ARGSUSED*/
static int
_exec_nis_cb(int instatus,
    char *inkey,
    int inkeylen,
    char *inval,
    int invallen,
    void *indata)
{
	int		check_policy = 1; /* always check policy for yp_all */
	int		stop_cb;
	const char	*filter;
	nss_status_t	res;
	_exec_nis_args	*eargp = (_exec_nis_args *)indata;
	nss_XbyY_args_t	*argp = eargp->argp;
	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);

	if (instatus != YP_TRUE) {
		*(eargp->yp_status) = YPERR_YPERR;
		return (0);	/* yp_all may decide otherwise... */
	}

	filter = (_priv_exec->name) ? _priv_exec->name : _priv_exec->id;

	/*
	 * yp_all does not null terminate the entry it retrieves from the
	 * map, unlike yp_match. so we do it explicitly here.
	 */
	inval[invallen] = '\0';

	/*
	 * Optimization:  if the entry doesn't contain the filter string then
	 * it can't be the entry we want, so don't bother looking more closely
	 * at it.
	 */
	if ((_priv_exec->policy &&
	    (strstr(inval, _priv_exec->policy) == NULL)) ||
	    (strstr(inval, filter) == NULL)) {
		*(eargp->yp_status) = YPERR_KEY;
		return (0);
	}

	res = _exec_nis_parse(inval, invallen, argp, check_policy);

	switch (res) {
	case NSS_SUCCESS:
		*(eargp->yp_status) = 0;
		stop_cb = (_priv_exec->search_flag == GET_ONE);
		break;
	case NSS_UNAVAIL:
		*(eargp->yp_status) = YPERR_KEY;
		stop_cb = 1;
		break;
	default:
		*(eargp->yp_status) = YPERR_YPERR;
		stop_cb = 0;
		break;
	}

	return (stop_cb);
}

static nss_status_t
_exec_nis_lookup(nis_backend_ptr_t be, nss_XbyY_args_t *argp, int getby_flag)
{
	int		ypstatus;
	nss_status_t	res = NSS_SUCCESS;
	nss_status_t	ypres;
	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);

	if (getby_flag == NSS_DBOP_EXECATTR_BYNAMEID) {
		int		check_policy = 0;
		int		vallen;
		char		*val;
		char		key[MAX_INPUT];

		/*
		 * Try using policy as part of search key. If that fails,
		 * (it will, in case of pre-Solaris9 nis server where policy
		 * was not searchable), try again without using policy.
		 */
		if (snprintf(key, MAX_INPUT, "%s%s%s%s%s", _priv_exec->name,
		    KV_TOKEN_DELIMIT, _priv_exec->policy, KV_TOKEN_DELIMIT,
		    _priv_exec->id) >= MAX_INPUT)
			return (NSS_NOTFOUND);
		do {
			ypres = _nss_nis_ypmatch(be->domain, NIS_MAP_EXECATTR,
			    key, &val, &vallen, &ypstatus);
			if ((check_policy == 0) && (ypstatus == YPERR_KEY)) {
				(void) snprintf(key, MAX_INPUT, "%s%s%s",
				    _priv_exec->name, KV_TOKEN_DELIMIT,
				    _priv_exec->id);
				check_policy = 1;
				continue;
			} else if (ypres != NSS_SUCCESS) {
				res = ypres;
				break;
			} else {
				char *val_save = val;

				massage_netdb((const char **)&val, &vallen);
				res = _exec_nis_parse((const char *)val,
				    vallen, argp, check_policy);
				free(val_save);
				break;
			}
		} while (res == NSS_SUCCESS);
	} else {
		int			ypstat = YPERR_YPERR;
		struct ypall_callback	cback;
		_exec_nis_args		eargs;

		eargs.yp_status = &ypstat;
		eargs.argp = argp;

		cback.foreach = _exec_nis_cb;
		cback.data = (void *)&eargs;

		/*
		 * Instead of calling yp_all() doing hard lookup, we use
		 * the alternative function, __yp_all_cflookup(), to
		 * perform soft lookup when binding to nis servers with
		 * time-out control. Other than that, these two functions
		 * do exactly the same thing.
		 */
		ypstatus = __yp_all_cflookup((char *)(be->domain),
			(char *)(be->enum_map), &cback, 0);

		/*
		 * For GET_ALL, check if we found anything at all.
		 */
		if (_priv_exec->head_exec != NULL)
			return (NSS_SUCCESS);

		switch (ypstat) {
		case 0:
			res = NSS_SUCCESS;
			break;
		case YPERR_BUSY:
			res = NSS_TRYAGAIN;
			break;
		default:
			res = NSS_UNAVAIL;
			break;
		}

	}

	return (res);
}

/*
 * If search for exact match for id failed, get_wild checks if we have
 * a wild-card entry for that id.
 */
static  nss_status_t
get_wild(nis_backend_ptr_t be, nss_XbyY_args_t *argp, int getby_flag)
{
	const char	*orig_id;
	char		*old_id = NULL;
	char		*wild_id = NULL;
	nss_status_t	res = NSS_NOTFOUND;
	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);

	orig_id = _priv_exec->id;
	old_id = strdup(_priv_exec->id);
	wild_id = old_id;
	while ((wild_id = _exec_wild_id(wild_id, _priv_exec->type)) != NULL) {
		_priv_exec->id = wild_id;
		res = _exec_nis_lookup(be, argp, getby_flag);
		if (res == NSS_SUCCESS)
			break;
	}
	_priv_exec->id = orig_id;
	if (old_id)
		free(old_id);

	return (res);
}


static  nss_status_t
getbynam(nis_backend_ptr_t be, void *a)
{
	nss_status_t	res;
	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;

	res = _exec_nis_lookup(be, argp, NSS_DBOP_EXECATTR_BYNAME);

	_exec_cleanup(res, argp);

	return (res);
}

static  nss_status_t
getbyid(nis_backend_ptr_t be, void *a)
{
	nss_status_t	res;
	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
	/*LINTED*/
	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);

	res = _exec_nis_lookup(be, argp, NSS_DBOP_EXECATTR_BYID);

	if (res != NSS_SUCCESS)
		res = get_wild(be, argp, NSS_DBOP_EXECATTR_BYID);

	_exec_cleanup(res, argp);

	return (res);
}


static  nss_status_t
getbynameid(nis_backend_ptr_t be, void *a)
{
	nss_status_t	res;
	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
	/*LINTED*/
	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);

	res = _exec_nis_lookup(be, argp, NSS_DBOP_EXECATTR_BYNAMEID);

	if (res != NSS_SUCCESS)
		res = get_wild(be, argp, NSS_DBOP_EXECATTR_BYNAMEID);

	_exec_cleanup(res, argp);

	return (res);
}


static nis_backend_op_t execattr_ops[] = {
	_nss_nis_destr,
	_nss_nis_endent,
	_nss_nis_setent,
	_nss_nis_getent_netdb,
	getbynam,
	getbyid,
	getbynameid
};

/*ARGSUSED*/
nss_backend_t *
_nss_nis_exec_attr_constr(const char *dummy1,
    const char *dummy2,
    const char *dummy3,
    const char *dummy4,
    const char *dummy5,
    const char *dummy6,
    const char *dummy7)
{
	return (_nss_nis_constr(execattr_ops,
		sizeof (execattr_ops)/sizeof (execattr_ops[0]),
		NIS_MAP_EXECATTR));
}