1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <stdlib.h>
27#include <string.h>
28#include <sys/types.h>
29#include <exec_attr.h>
30#include <rpcsvc/ypclnt.h>
31#include <rpcsvc/yp_prot.h>
32#include "nis_common.h"
33
34
35/* extern from nis_common.c */
36extern void massage_netdb(const char **, int *);
37/* externs from libnsl */
38extern int _doexeclist(nss_XbyY_args_t *);
39extern char *_exec_wild_id(char *, const char *);
40extern void _exec_cleanup(nss_status_t, nss_XbyY_args_t *);
41extern char *_strtok_escape(char *, char *, char **);
42
43typedef struct __exec_nis_args {
44	int		*yp_status;
45	nss_XbyY_args_t	*argp;
46} _exec_nis_args;
47
48
49/*
50 * check_match: returns 1 if -  matching entry found and no more entries needed,
51 *				or, entry cannot be found because of error;
52 *		returns 0 if -  no matching entry found, or,
53 *				matching entry found and next match needed.
54 */
55static int
56check_match(nss_XbyY_args_t *argp, int check_policy)
57{
58	execstr_t	*exec = (execstr_t *)(argp->returnval);
59	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
60	const char	*name = _priv_exec->name;
61	const char	*type = _priv_exec->type;
62	const char	*id = _priv_exec->id;
63	const char	*policy = _priv_exec->policy;
64
65	if (name && id) {
66		/*
67		 * NSS_DBOP_EXECATTR_BYNAMEID searched for name and id in
68		 * _exec_nis_lookup already.
69		 * If we're talking to pre-Solaris9 nis servers, check policy,
70		 * as policy was not a searchable column then.
71		 */
72		if ((check_policy && policy &&
73		    (strcmp(policy, exec->policy) != 0)) ||
74		    (type && (strcmp(type, exec->type) != 0))) {
75			return (0);
76		}
77	} else if ((policy && exec->policy &&
78	    (strcmp(policy, exec->policy) != 0)) ||
79	    (name && exec->name && (strcmp(name, exec->name) != 0)) ||
80	    (type && exec->type && (strcmp(type, exec->type) != 0)) ||
81	    (id && exec->id && (strcmp(id, exec->id) != 0))) {
82		return (0);
83	}
84
85	return (1);
86}
87
88/*
89 * check_match_strbuf: set up the data needed by check_match()
90 * and call it to match exec_attr data in strbuf and argp->key.attrp
91 */
92static int
93check_match_strbuf(nss_XbyY_args_t *argp, char *strbuf, int check_policy)
94{
95	char		*last = NULL;
96	char		*sep = KV_TOKEN_DELIMIT;
97	execstr_t	exec;
98	execstr_t	*execp = &exec;
99	void		*sp;
100	int		rc;
101
102	/*
103	 * Remove newline that yp_match puts at the
104	 * end of the entry it retrieves from the map.
105	 */
106	if (strbuf[argp->returnlen] == '\n') {
107		strbuf[argp->returnlen] = '\0';
108	}
109
110	execp->name = _strtok_escape(strbuf, sep, &last);
111	execp->policy = _strtok_escape(NULL, sep, &last);
112	execp->type = _strtok_escape(NULL, sep, &last);
113	execp->res1 = _strtok_escape(NULL, sep, &last);
114	execp->res2 = _strtok_escape(NULL, sep, &last);
115	execp->id = _strtok_escape(NULL, sep, &last);
116
117	sp = argp->returnval;
118	argp->returnval = execp;
119	rc = check_match(argp, check_policy);
120	argp->returnval = sp;
121	free(strbuf);
122
123	return (rc);
124}
125
126static  nss_status_t
127_exec_nis_parse(const char *instr,
128    int instr_len,
129    nss_XbyY_args_t *argp,
130    int check_policy)
131{
132	int		parse_stat;
133	nss_status_t	res;
134	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
135	char		*strbuf;
136	int		check_matched;
137
138	argp->returnval = NULL;
139	argp->returnlen = 0;
140	parse_stat = (*argp->str2ent)(instr, instr_len, argp->buf.result,
141	    argp->buf.buffer, argp->buf.buflen);
142	switch (parse_stat) {
143	case NSS_STR_PARSE_SUCCESS:
144		argp->returnlen = instr_len;
145		/* if exec_attr file format requested */
146		if (argp->buf.result == NULL) {
147			argp->returnval = argp->buf.buffer;
148			if ((strbuf = strdup(instr)) == NULL)
149				res = NSS_UNAVAIL;
150			check_matched = check_match_strbuf(argp,
151			    strbuf, check_policy);
152		} else {
153			argp->returnval = argp->buf.result;
154			check_matched = check_match(argp, check_policy);
155		}
156		if (check_matched) {
157			res = NSS_SUCCESS;
158			if (IS_GET_ALL(_priv_exec->search_flag)) {
159				if (_doexeclist(argp) == 0) {
160					res = NSS_UNAVAIL;
161				}
162			}
163		} else {
164			res = NSS_NOTFOUND;
165		}
166		break;
167	case NSS_STR_PARSE_ERANGE:
168		argp->erange = 1;
169		res = NSS_NOTFOUND;
170		break;
171	default:
172		res = NSS_UNAVAIL;
173		break;
174	}
175
176	return (res);
177}
178
179/*
180 * This is the callback for yp_all. It returns 0 to indicate that it wants to
181 * be called again for further key-value pairs, or returns non-zero to stop the
182 * flow of key-value pairs. If it returns a non-zero value, it is not called
183 * again. The functional value of yp_all is then 0.
184 */
185/*ARGSUSED*/
186static int
187_exec_nis_cb(int instatus,
188    char *inkey,
189    int inkeylen,
190    char *inval,
191    int invallen,
192    void *indata)
193{
194	int		check_policy = 1; /* always check policy for yp_all */
195	int		stop_cb;
196	const char	*filter;
197	nss_status_t	res;
198	_exec_nis_args	*eargp = (_exec_nis_args *)indata;
199	nss_XbyY_args_t	*argp = eargp->argp;
200	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
201
202	if (instatus != YP_TRUE) {
203		/*
204		 * If we have no more data to look at, we want to
205		 * keep yp_status from previous key/value pair
206		 * that we processed.
207		 * If this is the 1st time we enter this callback,
208		 * yp_status is already set to YPERR_YPERR
209		 * (see _exec_nis_lookup() for when this callback
210		 * and arguments are set initially).
211		 */
212		if (instatus != YP_NOMORE) {
213			*(eargp->yp_status) = YPERR_YPERR;
214		}
215		return (0);	/* yp_all may decide otherwise... */
216	}
217
218	filter = (_priv_exec->name) ? _priv_exec->name : _priv_exec->id;
219
220	/*
221	 * yp_all does not null terminate the entry it retrieves from the
222	 * map, unlike yp_match. so we do it explicitly here.
223	 */
224	inval[invallen] = '\0';
225
226	/*
227	 * Optimization:  if the entry doesn't contain the filter string then
228	 * it can't be the entry we want, so don't bother looking more closely
229	 * at it.
230	 */
231	if ((_priv_exec->policy &&
232	    (strstr(inval, _priv_exec->policy) == NULL)) ||
233	    (strstr(inval, filter) == NULL)) {
234		*(eargp->yp_status) = YPERR_KEY;
235		return (0);
236	}
237
238	res = _exec_nis_parse(inval, invallen, argp, check_policy);
239
240	switch (res) {
241	case NSS_SUCCESS:
242		*(eargp->yp_status) = 0;
243		stop_cb = IS_GET_ONE(_priv_exec->search_flag);
244		break;
245	case NSS_UNAVAIL:
246		*(eargp->yp_status) = YPERR_KEY;
247		stop_cb = 1;
248		break;
249	default:
250		*(eargp->yp_status) = YPERR_YPERR;
251		stop_cb = 0;
252		break;
253	}
254
255	return (stop_cb);
256}
257
258static nss_status_t
259_exec_nis_lookup(nis_backend_ptr_t be, nss_XbyY_args_t *argp, int getby_flag)
260{
261	int		ypstatus;
262	nss_status_t	res = NSS_SUCCESS;
263	nss_status_t	ypres;
264	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
265
266	if (getby_flag == NSS_DBOP_EXECATTR_BYNAMEID) {
267		int		check_policy = 0;
268		int		vallen;
269		char		*val;
270		char		key[MAX_INPUT];
271
272		/*
273		 * Try using policy as part of search key. If that fails,
274		 * (it will, in case of pre-Solaris9 nis server where policy
275		 * was not searchable), try again without using policy.
276		 */
277		if (snprintf(key, MAX_INPUT, "%s%s%s%s%s", _priv_exec->name,
278		    KV_TOKEN_DELIMIT, _priv_exec->policy, KV_TOKEN_DELIMIT,
279		    _priv_exec->id) >= MAX_INPUT)
280			return (NSS_NOTFOUND);
281		do {
282			ypres = _nss_nis_ypmatch(be->domain, NIS_MAP_EXECATTR,
283			    key, &val, &vallen, &ypstatus);
284			if ((check_policy == 0) && (ypstatus == YPERR_KEY)) {
285				(void) snprintf(key, MAX_INPUT, "%s%s%s",
286				    _priv_exec->name, KV_TOKEN_DELIMIT,
287				    _priv_exec->id);
288				check_policy = 1;
289				continue;
290			} else if (ypres != NSS_SUCCESS) {
291				res = ypres;
292				break;
293			} else {
294				char *val_save = val;
295
296				massage_netdb((const char **)&val, &vallen);
297				res = _exec_nis_parse((const char *)val,
298				    vallen, argp, check_policy);
299				free(val_save);
300				break;
301			}
302		} while (res == NSS_SUCCESS);
303	} else {
304		int			ypstat = YPERR_YPERR;
305		struct ypall_callback	cback;
306		_exec_nis_args		eargs;
307
308		eargs.yp_status = &ypstat;
309		eargs.argp = argp;
310
311		cback.foreach = _exec_nis_cb;
312		cback.data = (void *)&eargs;
313
314		/*
315		 * Instead of calling yp_all() doing hard lookup, we use
316		 * the alternative function, __yp_all_cflookup(), to
317		 * perform soft lookup when binding to nis servers with
318		 * time-out control. Other than that, these two functions
319		 * do exactly the same thing.
320		 */
321		ypstatus = __yp_all_cflookup((char *)(be->domain),
322		    (char *)(be->enum_map), &cback, 0);
323
324		/*
325		 * For GET_ALL, check if we found anything at all.
326		 */
327		if (_priv_exec->head_exec != NULL)
328			return (NSS_SUCCESS);
329
330		switch (ypstat) {
331		case 0:
332			res = NSS_SUCCESS;
333			break;
334		case YPERR_BUSY:
335			res = NSS_TRYAGAIN;
336			break;
337		case YPERR_KEY:
338			/*
339			 * If no such key, return NSS_NOTFOUND
340			 * as this looks more relevant; it will
341			 * also help libnsl to try with another
342			 * policy (see _getexecprof()).
343			 */
344			res = NSS_NOTFOUND;
345			break;
346		default:
347			res = NSS_UNAVAIL;
348			break;
349		}
350
351	}
352
353	return (res);
354}
355
356/*
357 * If search for exact match for id failed, get_wild checks if we have
358 * a wild-card entry for that id.
359 */
360static  nss_status_t
361get_wild(nis_backend_ptr_t be, nss_XbyY_args_t *argp, int getby_flag)
362{
363	const char	*orig_id;
364	char		*old_id = NULL;
365	char		*wild_id = NULL;
366	nss_status_t	res = NSS_NOTFOUND;
367	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
368
369	orig_id = _priv_exec->id;
370	old_id = strdup(_priv_exec->id);
371	wild_id = old_id;
372	while ((wild_id = _exec_wild_id(wild_id, _priv_exec->type)) != NULL) {
373		_priv_exec->id = wild_id;
374		res = _exec_nis_lookup(be, argp, getby_flag);
375		if (res == NSS_SUCCESS)
376			break;
377	}
378	_priv_exec->id = orig_id;
379	if (old_id)
380		free(old_id);
381
382	return (res);
383}
384
385
386static  nss_status_t
387getbynam(nis_backend_ptr_t be, void *a)
388{
389	nss_status_t	res;
390	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
391
392	res = _exec_nis_lookup(be, argp, NSS_DBOP_EXECATTR_BYNAME);
393
394	_exec_cleanup(res, argp);
395
396	return (res);
397}
398
399static  nss_status_t
400getbyid(nis_backend_ptr_t be, void *a)
401{
402	nss_status_t	res;
403	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
404	/*LINTED*/
405	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
406
407	res = _exec_nis_lookup(be, argp, NSS_DBOP_EXECATTR_BYID);
408
409	if (res != NSS_SUCCESS)
410		res = get_wild(be, argp, NSS_DBOP_EXECATTR_BYID);
411
412	_exec_cleanup(res, argp);
413
414	return (res);
415}
416
417
418static  nss_status_t
419getbynameid(nis_backend_ptr_t be, void *a)
420{
421	nss_status_t	res;
422	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
423	/*LINTED*/
424	_priv_execattr	*_priv_exec = (_priv_execattr *)(argp->key.attrp);
425
426	res = _exec_nis_lookup(be, argp, NSS_DBOP_EXECATTR_BYNAMEID);
427
428	if (res != NSS_SUCCESS)
429		res = get_wild(be, argp, NSS_DBOP_EXECATTR_BYNAMEID);
430
431	_exec_cleanup(res, argp);
432
433	return (res);
434}
435
436
437static nis_backend_op_t execattr_ops[] = {
438	_nss_nis_destr,
439	_nss_nis_endent,
440	_nss_nis_setent,
441	_nss_nis_getent_netdb,
442	getbynam,
443	getbyid,
444	getbynameid
445};
446
447/*ARGSUSED*/
448nss_backend_t *
449_nss_nis_exec_attr_constr(const char *dummy1,
450    const char *dummy2,
451    const char *dummy3,
452    const char *dummy4,
453    const char *dummy5,
454    const char *dummy6,
455    const char *dummy7)
456{
457	return (_nss_nis_constr(execattr_ops,
458	    sizeof (execattr_ops)/sizeof (execattr_ops[0]),
459	    NIS_MAP_EXECATTR));
460}
461