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