xref: /titanic_51/usr/src/lib/nsswitch/ldap/common/getservent.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <ctype.h>
30 #include <netdb.h>
31 #include "ns_internal.h"
32 #include "ldap_common.h"
33 
34 /* services attributes filters */
35 #define	_S_NAME			"cn"
36 #define	_S_PORT			"ipserviceport"
37 #define	_S_PROTOCOL		"ipserviceprotocol"
38 #define	_F_GETSERVBYNAME	"(&(objectClass=ipService)(cn=%s))"
39 #define	_F_GETSERVBYNAME_SSD	"(&(%%s)(cn=%s))"
40 #define	_F_GETSERVBYNAMEPROTO	\
41 	"(&(objectClass=ipService)(cn=%s)(ipServiceProtocol=%s))"
42 #define	_F_GETSERVBYNAMEPROTO_SSD	\
43 	"(&(%%s)(cn=%s)(ipServiceProtocol=%s))"
44 #define	_F_GETSERVBYPORT	"(&(objectClass=ipService)(ipServicePort=%ld))"
45 #define	_F_GETSERVBYPORT_SSD	"(&(%%s)(ipServicePort=%ld))"
46 #define	_F_GETSERVBYPORTPROTO	\
47 	"(&(objectClass=ipService)(ipServicePort=%ld)(ipServiceProtocol=%s))"
48 #define	_F_GETSERVBYPORTPROTO_SSD	\
49 	"(&(%%s)(ipServicePort=%ld)(ipServiceProtocol=%s))"
50 
51 typedef struct _nss_services_cookie {
52 	int			index;	/* index of ipserviceprotocol */
53 	char			*cname;	/* canonical name, don't free it */
54 	ns_ldap_result_t	*result;
55 } _nss_services_cookie_t;
56 
57 static const char *services_attrs[] = {
58 	_S_NAME,
59 	_S_PORT,
60 	_S_PROTOCOL,
61 	(char *)NULL
62 };
63 
64 void
65 _nss_services_cookie_free(void **ckP) {
66 	_nss_services_cookie_t **cookieP = (_nss_services_cookie_t **)ckP;
67 	if (cookieP && *cookieP) {
68 		if ((*cookieP)->result)
69 			(void) __ns_ldap_freeResult(&(*cookieP)->result);
70 		free(*cookieP);
71 		*cookieP = NULL;
72 	}
73 }
74 
75 static _nss_services_cookie_t *
76 _nss_services_cookie_new(ns_ldap_result_t *result, int index, char *cname) {
77 
78 	_nss_services_cookie_t	*cookie;
79 
80 	if ((cookie = calloc(1, sizeof (*cookie))) == NULL)
81 		return (NULL);
82 
83 	/*
84 	 * result has been allocated either by __ns_ldap_firstEntry
85 	 * or __ns_ldap_nextEntry.
86 	 */
87 	cookie->result = result;
88 
89 	cookie->index = index;
90 	cookie->cname = cname;
91 
92 	return (cookie);
93 }
94 
95 /*
96  * _nss_ldap_services2ent is the data marshaling method for the services
97  * getXbyY * (e.g., getbyname(), getbyport(), getent()) backend processes.
98  * This method is called after a successful ldap search has been performed.
99  * This method will parse the ldap search values into *serv = (struct
100  * servent *)argp->buf.result which the frontend process expects. Three error
101  * conditions are expected and returned to nsswitch.
102  *
103  * In section 5.5 of RFC 2307, it specifies that a "services" LDAP entry
104  * containing multiple ipserviceprotocol values should be able to be mapped
105  * to multiple "services" entities. Code has been added to support
106  * this one to many mapping feature.
107  */
108 
109 static int
110 _nss_ldap_services2ent(ldap_backend_ptr be, nss_XbyY_args_t *argp)
111 {
112 	int		i, j, k;
113 	int		nss_result;
114 	int		buflen = (int)0;
115 	int		firstime = (int)1;
116 	unsigned long	len = 0L;
117 	char		**mp, *cname = NULL, *protoval = NULL;
118 	char		*buffer = (char *)NULL;
119 	char		*ceiling = (char *)NULL;
120 	struct servent *serv = (struct servent *)NULL;
121 	ns_ldap_result_t	*result;
122 	ns_ldap_attr_t	*attrptr, *protocol = NULL;
123 	_nss_services_cookie_t	*cookie = (_nss_services_cookie_t *)
124 						be->services_cookie;
125 
126 	buffer = (char *)argp->buf.buffer;
127 	buflen = (size_t)argp->buf.buflen;
128 	serv = (struct servent *)argp->buf.result;
129 	ceiling = buffer + buflen;
130 #ifdef DEBUG
131 	(void) fprintf(stderr, "[getservent.c: _nss_ldap_services2ent]\n");
132 #endif /* DEBUG */
133 
134 	if (cookie) {
135 		/*
136 		 * getservent_r with multiple protocol values and the entry
137 		 * is enumerated 2nd time or beyond
138 		 */
139 		result =  cookie->result;
140 		cname = cookie->cname;
141 	} else {
142 		/*
143 		 * getservbyname_r, getservbyport_r or
144 		 * getservent_r with single protocol value or multiple values
145 		 * and the entry is enumerated 1st time
146 		 */
147 		result = be->result;
148 	}
149 
150 	nss_result = (int)NSS_STR_PARSE_SUCCESS;
151 	(void) memset(argp->buf.buffer, 0, buflen);
152 
153 	attrptr = getattr(result, 0);
154 	if (attrptr == NULL) {
155 		nss_result = (int)NSS_STR_PARSE_PARSE;
156 		goto result_srvs2ent;
157 	}
158 	for (i = 0; i < result->entry->attr_count; i++) {
159 		attrptr = getattr(result, i);
160 		if (attrptr == NULL) {
161 			nss_result = (int)NSS_STR_PARSE_PARSE;
162 			goto result_srvs2ent;
163 		}
164 		if (strcasecmp(attrptr->attrname, _S_NAME) == 0) {
165 			for (j = 0; j < attrptr->value_count; j++) {
166 				if (firstime) {
167 					/* service name */
168 					if (cname == NULL) {
169 					    cname = __s_api_get_canonical_name(
170 					    result->entry, attrptr, 1);
171 					}
172 					if (cname == NULL ||
173 						(len = strlen(cname)) < 1) {
174 						nss_result =
175 							NSS_STR_PARSE_PARSE;
176 						goto result_srvs2ent;
177 					}
178 					serv->s_name = buffer;
179 					buffer += len + 1;
180 					if (buffer >= ceiling) {
181 						nss_result =
182 						    (int)NSS_STR_PARSE_ERANGE;
183 						goto result_srvs2ent;
184 					}
185 					(void) strcpy(serv->s_name, cname);
186 					/* alias list */
187 					mp = serv->s_aliases =
188 						(char **)ROUND_UP(buffer,
189 						sizeof (char **));
190 					buffer = (char *)serv->s_aliases +
191 						sizeof (char *) *
192 						(attrptr->value_count + 1);
193 					buffer = (char *)ROUND_UP(buffer,
194 						sizeof (char **));
195 					if (buffer >= ceiling) {
196 						nss_result =
197 						    (int)NSS_STR_PARSE_ERANGE;
198 						goto result_srvs2ent;
199 					}
200 					firstime = (int)0;
201 				}
202 				/* alias list */
203 				if ((attrptr->attrvalue[j] == NULL) ||
204 				    (len = strlen(attrptr->attrvalue[j])) < 1) {
205 					nss_result = (int)NSS_STR_PARSE_PARSE;
206 					goto result_srvs2ent;
207 				}
208 				/* skip canonical name */
209 				if (strcmp(cname, attrptr->attrvalue[j]) == 0)
210 					continue;
211 
212 				*mp = buffer;
213 				buffer += len + 1;
214 				if (buffer >= ceiling) {
215 					nss_result = (int)NSS_STR_PARSE_ERANGE;
216 					goto result_srvs2ent;
217 				}
218 				(void) strcpy(*mp++, attrptr->attrvalue[j]);
219 				continue;
220 			}
221 		}
222 
223 		if (strcasecmp(attrptr->attrname, _S_PORT) == 0) {
224 			if ((attrptr->attrvalue[0] == NULL) ||
225 			    (len = strlen(attrptr->attrvalue[0])) < 1) {
226 				nss_result = (int)NSS_STR_PARSE_PARSE;
227 				goto result_srvs2ent;
228 			}
229 			serv->s_port =
230 			    htons((ushort_t)atoi(attrptr->attrvalue[0]));
231 			continue;
232 		}
233 
234 		if (strcasecmp(attrptr->attrname, _S_PROTOCOL) == 0) {
235 			/* protocol name */
236 			if (attrptr->attrvalue == NULL) {
237 				nss_result = (int)NSS_STR_PARSE_PARSE;
238 				goto result_srvs2ent;
239 			}
240 			protocol = attrptr;
241 			if (cookie) {
242 				/*
243 				 * getservent_r
244 				 * Get current value then increment index
245 				 */
246 				protoval = attrptr->attrvalue[cookie->index++];
247 			} else if (attrptr->value_count > 1 &&
248 						argp->key.serv.proto) {
249 				/*
250 				 * getserverbyname_r and getservbyport_r
251 				 *
252 				 * If there are more than one value and
253 				 * it needs to match protocol too,
254 				 * iterate each value to find matching one.
255 				 * getservent_r sets key.serv.proto to NULL,
256 				 * so it wouldn't run this part of code.
257 				 */
258 				for (k = 0; k < attrptr->value_count; k++) {
259 					if (attrptr->attrvalue[k] == NULL) {
260 						nss_result =
261 							NSS_STR_PARSE_PARSE;
262 						goto result_srvs2ent;
263 					}
264 					if (strcmp(attrptr->attrvalue[k],
265 						argp->key.serv.proto) == 0) {
266 						protoval =
267 							attrptr->attrvalue[k];
268 						break;
269 					}
270 				}
271 			} else {
272 				/*
273 				 * 1. getserverbyname_r and getservbyport_r
274 				 *
275 				 * It does not need to match protocol or
276 				 * ipserviceprotocol has single value,
277 				 * return the first one
278 				 *
279 				 * 2. getservent_r with single value
280 				 * or multiple values and the entry is
281 				 * enumerated 1st time,
282 				 * return the first one
283 				 *
284 				 */
285 				protoval = attrptr->attrvalue[0];
286 			}
287 
288 			if (protoval == NULL || (len = strlen(protoval)) < 1) {
289 				nss_result = (int)NSS_STR_PARSE_PARSE;
290 				goto result_srvs2ent;
291 			}
292 			serv->s_proto = buffer;
293 			buffer += len + 1;
294 			if (buffer >= ceiling) {
295 				nss_result = (int)NSS_STR_PARSE_ERANGE;
296 				goto result_srvs2ent;
297 			}
298 			(void) strcpy(serv->s_proto, protoval);
299 			continue;
300 		}
301 	}
302 
303 	if (be->enumcookie != NULL && cookie == NULL &&
304 			protocol->value_count > 1) {
305 		/*
306 		 * getservent_r with multiple ipserviceprotocol values
307 		 * and the entry is enumerated 1st time
308 		 *
309 		 * Create cookie and save result in the cookie
310 		 * "attrvalue[0]" of ipserviceprotocol is returned,
311 		 * so it starts with index 1. Also save the canonical name.
312 		 */
313 		be->services_cookie =
314 			(void *)_nss_services_cookie_new(be->result, 1, cname);
315 		if (be->services_cookie == NULL) {
316 			nss_result = NSS_STR_PARSE_PARSE;
317 			goto result_srvs2ent;
318 		}
319 
320 		/* reset be->result so it won't get freed later */
321 		be->result = NULL;
322 	}
323 
324 #ifdef DEBUG
325 	(void) fprintf(stdout, "\n[getservent.c: _nss_ldap_services2ent]\n");
326 	(void) fprintf(stdout, "        s_name: [%s]\n", serv->s_name);
327 	if (mp != NULL) {
328 		for (mp = serv->s_aliases; *mp != NULL; mp++)
329 			(void) fprintf(stdout, "     s_aliases: [%s]\n", *mp);
330 	}
331 	(void) fprintf(stdout, "        s_port: [%d]\n", serv->s_port);
332 	(void) fprintf(stdout, "    s_protocol: [%s]\n", serv->s_proto);
333 #endif /* DEBUG */
334 
335 result_srvs2ent:
336 	if (cookie) {
337 		/*
338 		 * getservent_r with multiple ipserviceprotocol values and
339 		 * the entry is enumerated 2nd time or beyond
340 		 */
341 		if (nss_result != NSS_STR_PARSE_SUCCESS ||
342 			cookie->index >= protocol->value_count) {
343 			/*
344 			 * If it's an error case or it has iterated all
345 			 * ipservicesprotocol value(s) then free cookie and
346 			 * set it to NULL
347 			 *
348 			 */
349 			_nss_services_cookie_free(
350 				(void **)&be->services_cookie);
351 		}
352 	} else {
353 		/*
354 		 * getservbyname_r, getservbyport_r, or
355 		 * getservent_r with single value or can't create cookie
356 		 */
357 		(void) __ns_ldap_freeResult(&be->result);
358 	}
359 	return ((int)nss_result);
360 }
361 
362 
363 /*
364  * getbyname gets struct servent values by service name. This
365  * function constructs an ldap search filter using the service
366  * name invocation parameter and the getservbyname search filter
367  * defined. Once the filter is constructed, we search for a matching
368  * entry and marshal the data results into *serv = (struct servent *)
369  * argp->buf.result. The function _nss_ldap_services2ent performs
370  * the data marshaling.
371  */
372 
373 static nss_status_t
374 getbyname(ldap_backend_ptr be, void *a)
375 {
376 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
377 	const char	*proto = argp->key.serv.proto;
378 	char		searchfilter[SEARCHFILTERLEN];
379 	char		userdata[SEARCHFILTERLEN];
380 	char		name[SEARCHFILTERLEN];
381 	char		protocol[SEARCHFILTERLEN];
382 	int		ret;
383 
384 	if (_ldap_filter_name(name, argp->key.serv.serv.name, sizeof (name))
385 			!= 0)
386 		return ((nss_status_t)NSS_NOTFOUND);
387 
388 	if (proto == NULL) {
389 		ret = snprintf(searchfilter, sizeof (searchfilter),
390 		    _F_GETSERVBYNAME, name);
391 		if (ret >= sizeof (searchfilter) || ret < 0)
392 			return ((nss_status_t)NSS_NOTFOUND);
393 
394 		ret = snprintf(userdata, sizeof (userdata),
395 		    _F_GETSERVBYNAME_SSD, name);
396 		if (ret >= sizeof (userdata) || ret < 0)
397 			return ((nss_status_t)NSS_NOTFOUND);
398 	} else {
399 		if (_ldap_filter_name(protocol, proto, sizeof (protocol)) != 0)
400 			return ((nss_status_t)NSS_NOTFOUND);
401 
402 		ret = snprintf(searchfilter, sizeof (searchfilter),
403 		    _F_GETSERVBYNAMEPROTO, name, protocol);
404 		if (ret >= sizeof (searchfilter) || ret < 0)
405 			return ((nss_status_t)NSS_NOTFOUND);
406 
407 		ret = snprintf(userdata, sizeof (userdata),
408 		    _F_GETSERVBYNAMEPROTO_SSD, name, protocol);
409 		if (ret >= sizeof (userdata) || ret < 0)
410 			return ((nss_status_t)NSS_NOTFOUND);
411 	}
412 
413 	return ((nss_status_t)_nss_ldap_lookup(be, argp,
414 		_SERVICES, searchfilter, NULL,
415 		_merge_SSD_filter, userdata));
416 }
417 
418 
419 /*
420  * getbyport gets struct servent values by service port. This
421  * function constructs an ldap search filter using the service
422  * name invocation parameter and the getservbyport search filter
423  * defined. Once the filter is constructed, we search for a matching
424  * entry and marshal the data results into *serv = (struct servent *)
425  * argp->buf.result. The function _nss_ldap_services2ent performs
426  * the data marshaling.
427  */
428 
429 static nss_status_t
430 getbyport(ldap_backend_ptr be, void *a)
431 {
432 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
433 	const char	*proto = argp->key.serv.proto;
434 	char		portstr[12];
435 	char		searchfilter[SEARCHFILTERLEN];
436 	char		userdata[SEARCHFILTERLEN];
437 	char		protocol[SEARCHFILTERLEN];
438 	int		ret;
439 
440 	ret = snprintf(portstr, sizeof (portstr), " %d",
441 	    ntohs((ushort_t)argp->key.serv.serv.port));
442 	if (ret >= sizeof (portstr) || ret < 0)
443 		return ((nss_status_t)NSS_NOTFOUND);
444 
445 	if (proto == NULL) {
446 		ret = snprintf(searchfilter, sizeof (searchfilter),
447 		    _F_GETSERVBYPORT, strtol(portstr, (char **)NULL, 10));
448 		if (ret >= sizeof (searchfilter) || ret < 0)
449 			return ((nss_status_t)NSS_NOTFOUND);
450 
451 		ret = snprintf(userdata, sizeof (userdata),
452 		    _F_GETSERVBYPORT_SSD, strtol(portstr, (char **)NULL, 10));
453 		if (ret >= sizeof (userdata) || ret < 0)
454 			return ((nss_status_t)NSS_NOTFOUND);
455 	} else {
456 		if (_ldap_filter_name(protocol, proto, sizeof (protocol)) != 0)
457 			return ((nss_status_t)NSS_NOTFOUND);
458 
459 		ret = snprintf(searchfilter, sizeof (searchfilter),
460 		    _F_GETSERVBYPORTPROTO,
461 		    strtol(portstr, (char **)NULL, 10), protocol);
462 		if (ret >= sizeof (searchfilter) || ret < 0)
463 			return ((nss_status_t)NSS_NOTFOUND);
464 
465 		ret = snprintf(userdata, sizeof (userdata),
466 		    _F_GETSERVBYPORTPROTO_SSD,
467 		    strtol(portstr, (char **)NULL, 10), protocol);
468 		if (ret >= sizeof (userdata) || ret < 0)
469 			return ((nss_status_t)NSS_NOTFOUND);
470 	}
471 
472 	return ((nss_status_t)_nss_ldap_lookup(be, argp,
473 		_SERVICES, searchfilter, NULL,
474 		_merge_SSD_filter, userdata));
475 }
476 
477 static ldap_backend_op_t serv_ops[] = {
478     _nss_ldap_destr,
479     _nss_ldap_endent,
480     _nss_ldap_setent,
481     _nss_ldap_getent,
482     getbyname,
483     getbyport
484 };
485 
486 
487 /*
488  * _nss_ldap_services_constr is where life begins. This function calls
489  * the generic ldap constructor function to define and build the
490  * abstract data types required to support ldap operations.
491  */
492 
493 /*ARGSUSED0*/
494 nss_backend_t *
495 _nss_ldap_services_constr(const char *dummy1, const char *dummy2,
496 			const char *dummy3)
497 {
498 
499 	return ((nss_backend_t *)_nss_ldap_constr(serv_ops,
500 		sizeof (serv_ops)/sizeof (serv_ops[0]), _SERVICES,
501 		services_attrs, _nss_ldap_services2ent));
502 }
503