xref: /illumos-gate/usr/src/lib/nsswitch/ldap/common/getservent.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
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 2006 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 <ctype.h>
29 #include <netdb.h>
30 #include "ns_internal.h"
31 #include "ldap_common.h"
32 
33 /* services attributes filters */
34 #define	_S_NAME			"cn"
35 #define	_S_PORT			"ipserviceport"
36 #define	_S_PROTOCOL		"ipserviceprotocol"
37 #define	_F_GETSERVBYNAME	"(&(objectClass=ipService)(cn=%s))"
38 #define	_F_GETSERVBYNAME_SSD	"(&(%%s)(cn=%s))"
39 #define	_F_GETSERVBYNAMEPROTO	\
40 	"(&(objectClass=ipService)(cn=%s)(ipServiceProtocol=%s))"
41 #define	_F_GETSERVBYNAMEPROTO_SSD	\
42 	"(&(%%s)(cn=%s)(ipServiceProtocol=%s))"
43 #define	_F_GETSERVBYPORT	"(&(objectClass=ipService)(ipServicePort=%ld))"
44 #define	_F_GETSERVBYPORT_SSD	"(&(%%s)(ipServicePort=%ld))"
45 #define	_F_GETSERVBYPORTPROTO	\
46 	"(&(objectClass=ipService)(ipServicePort=%ld)(ipServiceProtocol=%s))"
47 #define	_F_GETSERVBYPORTPROTO_SSD	\
48 	"(&(%%s)(ipServicePort=%ld)(ipServiceProtocol=%s))"
49 
50 typedef struct _nss_services_cookie {
51 	int			index;	/* index of ipserviceprotocol */
52 	char			*cname;	/* canonical name, don't free it */
53 	ns_ldap_result_t	*result;
54 } _nss_services_cookie_t;
55 
56 static const char *services_attrs[] = {
57 	_S_NAME,
58 	_S_PORT,
59 	_S_PROTOCOL,
60 	(char *)NULL
61 };
62 
63 void
64 _nss_services_cookie_free(void **ckP) {
65 	_nss_services_cookie_t **cookieP = (_nss_services_cookie_t **)ckP;
66 	if (cookieP && *cookieP) {
67 		if ((*cookieP)->result)
68 			(void) __ns_ldap_freeResult(&(*cookieP)->result);
69 		free(*cookieP);
70 		*cookieP = NULL;
71 	}
72 }
73 
74 static _nss_services_cookie_t *
75 _nss_services_cookie_new(ns_ldap_result_t *result, int index, char *cname) {
76 
77 	_nss_services_cookie_t	*cookie;
78 
79 	if ((cookie = calloc(1, sizeof (*cookie))) == NULL)
80 		return (NULL);
81 
82 	/*
83 	 * result has been allocated either by __ns_ldap_firstEntry
84 	 * or __ns_ldap_nextEntry.
85 	 */
86 	cookie->result = result;
87 
88 	cookie->index = index;
89 	cookie->cname = cname;
90 
91 	return (cookie);
92 }
93 /*
94  * _nss_ldap_services2str is the data marshaling method for the services
95  * getXbyY * (e.g., getbyname(), getbyport(), getent()) backend processes.
96  * This method is called after a successful ldap search has been performed.
97  * This method will parse the ldap search values into the file format.
98  * e.g.
99  *
100  * nfsd 2049/udp nfs
101  * nfsd 2049/tcp nfs
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_services2str(ldap_backend_ptr be, nss_XbyY_args_t *argp)
111 {
112 	uint_t		i, k;
113 	int		nss_result;
114 	int		buflen = 0, len;
115 	char		**ipport, *cname = NULL, *protoval = NULL;
116 	char		*buffer = NULL;
117 	ns_ldap_result_t	*result;
118 	ns_ldap_attr_t	*names = NULL, *protocol = NULL;
119 	_nss_services_cookie_t	*cookie = (_nss_services_cookie_t *)
120 						be->services_cookie;
121 
122 	if (cookie) {
123 		/*
124 		 * getservent_r with multiple protocol values and the entry
125 		 * is enumerated 2nd time or beyond
126 		 */
127 		result =  cookie->result;
128 		cname = cookie->cname;
129 	} else {
130 		/*
131 		 * getservbyname_r, getservbyport_r or
132 		 * getservent_r with single protocol value or multiple values
133 		 * and the entry is enumerated 1st time
134 		 */
135 		result = be->result;
136 	}
137 	if (result == NULL) {
138 		nss_result = NSS_STR_PARSE_PARSE;
139 		goto result_srvs2str;
140 	}
141 
142 	buflen = argp->buf.buflen;
143 	if (argp->buf.result != NULL) {
144 		if ((be->buffer = calloc(1, buflen)) == NULL) {
145 			nss_result = NSS_STR_PARSE_PARSE;
146 			goto result_srvs2str;
147 		}
148 		buffer = be->buffer;
149 	} else
150 		buffer = argp->buf.buffer;
151 
152 
153 	nss_result = NSS_STR_PARSE_SUCCESS;
154 	(void) memset(argp->buf.buffer, 0, buflen);
155 
156 	/* Get services names */
157 	names = __ns_ldap_getAttrStruct(result->entry, _S_NAME);
158 	if (names == NULL || names->attrvalue == NULL) {
159 		nss_result = NSS_STR_PARSE_PARSE;
160 		goto result_srvs2str;
161 	}
162 	/* Get canonical services name */
163 	if (cname == NULL) {
164 	    cname = __s_api_get_canonical_name(result->entry, names, 1);
165 	    if (cname == NULL || (len = strlen(cname)) < 1) {
166 		nss_result = NSS_STR_PARSE_PARSE;
167 		goto result_srvs2str;
168 	    }
169 	}
170 	/* Get port */
171 	ipport = __ns_ldap_getAttr(result->entry, _S_PORT);
172 	if (ipport == NULL || ipport[0] == NULL ||
173 			(len = strlen(cname)) < 1) {
174 		nss_result = NSS_STR_PARSE_PARSE;
175 		goto result_srvs2str;
176 	}
177 	/* Set services name and port and '/' */
178 	len = snprintf(buffer, buflen, "%s %s/", cname, ipport[0]);
179 	TEST_AND_ADJUST(len, buffer, buflen, result_srvs2str);
180 
181 	/* Get protocol */
182 	protocol = __ns_ldap_getAttrStruct(result->entry, _S_PROTOCOL);
183 	if (protocol == NULL || protocol->attrvalue == NULL) {
184 		nss_result = NSS_STR_PARSE_PARSE;
185 		goto result_srvs2str;
186 	}
187 
188 	if (cookie) {
189 		/*
190 		 * getservent_r
191 		 * Get current value then increment index
192 		 */
193 		protoval = protocol->attrvalue[cookie->index++];
194 	} else if (protocol->value_count > 1 && be->setcalled == 0 &&
195 			argp->key.serv.proto) {
196 		/*
197 		 * getserverbyname_r and getservbyport_r
198 		 *
199 		 * If there are more than one value and
200 		 * it needs to match protocol too,
201 		 * iterate each value to find matching one.
202 		 */
203 		for (k = 0; k < protocol->value_count; k++) {
204 			if (protocol->attrvalue[k] == NULL) {
205 				nss_result = NSS_STR_PARSE_PARSE;
206 				goto result_srvs2str;
207 			}
208 			if (strcmp(protocol->attrvalue[k],
209 				argp->key.serv.proto) == 0) {
210 				protoval = protocol->attrvalue[k];
211 				break;
212 			}
213 		}
214 	} else {
215 		/*
216 		 * 1. getserverbyname_r and getservbyport_r
217 		 *
218 		 * It does not need to match protocol or
219 		 * ipserviceprotocol has single value,
220 		 * return the first one.
221 		 *
222 		 * 2. getservent_r with single ipserviceprotocol value
223 		 * or multiple values and the entry is
224 		 * enumerated 1st time,  return the first one.
225 		 *
226 		 */
227 		protoval = protocol->attrvalue[0];
228 	}
229 
230 	if (protoval == NULL || (len = strlen(protoval)) < 1) {
231 		nss_result = NSS_STR_PARSE_PARSE;
232 		goto result_srvs2str;
233 	}
234 
235 	/* Set protocol */
236 	len = snprintf(buffer, buflen, "%s", protoval);
237 	TEST_AND_ADJUST(len, buffer, buflen, result_srvs2str);
238 
239 	/* Append aliases */
240 	for (i = 0; i < names->value_count; i++) {
241 		if (names->attrvalue[i] == NULL) {
242 			nss_result = NSS_STR_PARSE_PARSE;
243 			goto result_srvs2str;
244 		}
245 		/* Skip the canonical name */
246 		if (strcmp(cname, names->attrvalue[i]) != 0) {
247 			len = snprintf(buffer, buflen, " %s",
248 					names->attrvalue[i]);
249 			TEST_AND_ADJUST(len, buffer, buflen, result_srvs2str);
250 		}
251 	}
252 
253 
254 	if (be->enumcookie != NULL && cookie == NULL &&
255 			protocol->value_count > 1) {
256 		/*
257 		 * getservent_r with multiple ipserviceprotocol values
258 		 * and the entry is enumerated 1st time
259 		 *
260 		 * Create cookie and save result in the cookie
261 		 * "attrvalue[0]" of ipserviceprotocol is returned,
262 		 * so it starts with index 1. Also save the canonical name.
263 		 */
264 		be->services_cookie =
265 			(void *)_nss_services_cookie_new(be->result, 1, cname);
266 		if (be->services_cookie == NULL) {
267 			nss_result = NSS_STR_PARSE_PARSE;
268 			goto result_srvs2str;
269 		}
270 
271 		/* reset be->result so it won't get freed later */
272 		be->result = NULL;
273 	}
274 
275 	/* The front end marshaller doesn't need to copy trailing nulls */
276 	if (argp->buf.result != NULL)
277 		be->buflen = strlen(be->buffer);
278 
279 result_srvs2str:
280 	if (cookie) {
281 		/*
282 		 * getservent_r with multiple ipserviceprotocol values and
283 		 * the entry is enumerated 2nd time or beyond
284 		 */
285 		if (nss_result != NSS_STR_PARSE_SUCCESS ||
286 			cookie->index >= protocol->value_count) {
287 			/*
288 			 * If it's an error case or it has iterated all
289 			 * ipservicesprotocol value(s) then free cookie and
290 			 * set it to NULL
291 			 *
292 			 */
293 			_nss_services_cookie_free(
294 				(void **)&be->services_cookie);
295 		}
296 	} else {
297 		/*
298 		 * getservbyname_r, getservbyport_r, or
299 		 * getservent_r with single value or can't create cookie
300 		 */
301 		(void) __ns_ldap_freeResult(&be->result);
302 	}
303 	return (nss_result);
304 }
305 
306 /*
307  * getbyname gets struct servent values by service name. This
308  * function constructs an ldap search filter using the service
309  * name invocation parameter and the getservbyname search filter
310  * defined. Once the filter is constructed, we search for a matching
311  * entry and marshal the data results into *serv = (struct servent *)
312  * argp->buf.result. The function _nss_ldap_services2ent performs
313  * the data marshaling.
314  */
315 
316 static nss_status_t
317 getbyname(ldap_backend_ptr be, void *a)
318 {
319 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
320 	const char	*proto = argp->key.serv.proto;
321 	char		searchfilter[SEARCHFILTERLEN];
322 	char		userdata[SEARCHFILTERLEN];
323 	char		name[SEARCHFILTERLEN];
324 	char		protocol[SEARCHFILTERLEN];
325 	int		ret;
326 
327 	if (_ldap_filter_name(name, argp->key.serv.serv.name, sizeof (name))
328 			!= 0)
329 		return ((nss_status_t)NSS_NOTFOUND);
330 
331 	if (proto == NULL) {
332 		ret = snprintf(searchfilter, sizeof (searchfilter),
333 		    _F_GETSERVBYNAME, name);
334 		if (ret >= sizeof (searchfilter) || ret < 0)
335 			return ((nss_status_t)NSS_NOTFOUND);
336 
337 		ret = snprintf(userdata, sizeof (userdata),
338 		    _F_GETSERVBYNAME_SSD, name);
339 		if (ret >= sizeof (userdata) || ret < 0)
340 			return ((nss_status_t)NSS_NOTFOUND);
341 	} else {
342 		if (_ldap_filter_name(protocol, proto, sizeof (protocol)) != 0)
343 			return ((nss_status_t)NSS_NOTFOUND);
344 
345 		ret = snprintf(searchfilter, sizeof (searchfilter),
346 		    _F_GETSERVBYNAMEPROTO, name, protocol);
347 		if (ret >= sizeof (searchfilter) || ret < 0)
348 			return ((nss_status_t)NSS_NOTFOUND);
349 
350 		ret = snprintf(userdata, sizeof (userdata),
351 		    _F_GETSERVBYNAMEPROTO_SSD, name, protocol);
352 		if (ret >= sizeof (userdata) || ret < 0)
353 			return ((nss_status_t)NSS_NOTFOUND);
354 	}
355 
356 	return ((nss_status_t)_nss_ldap_lookup(be, argp,
357 		_SERVICES, searchfilter, NULL,
358 		_merge_SSD_filter, userdata));
359 }
360 
361 
362 /*
363  * getbyport gets struct servent values by service port. This
364  * function constructs an ldap search filter using the service
365  * name invocation parameter and the getservbyport search filter
366  * defined. Once the filter is constructed, we search for a matching
367  * entry and marshal the data results into *serv = (struct servent *)
368  * argp->buf.result. The function _nss_ldap_services2ent performs
369  * the data marshaling.
370  */
371 
372 static nss_status_t
373 getbyport(ldap_backend_ptr be, void *a)
374 {
375 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
376 	const char	*proto = argp->key.serv.proto;
377 	char		portstr[12];
378 	char		searchfilter[SEARCHFILTERLEN];
379 	char		userdata[SEARCHFILTERLEN];
380 	char		protocol[SEARCHFILTERLEN];
381 	int		ret;
382 
383 	ret = snprintf(portstr, sizeof (portstr), " %d",
384 	    ntohs((ushort_t)argp->key.serv.serv.port));
385 	if (ret >= sizeof (portstr) || ret < 0)
386 		return ((nss_status_t)NSS_NOTFOUND);
387 
388 	if (proto == NULL) {
389 		ret = snprintf(searchfilter, sizeof (searchfilter),
390 		    _F_GETSERVBYPORT, strtol(portstr, (char **)NULL, 10));
391 		if (ret >= sizeof (searchfilter) || ret < 0)
392 			return ((nss_status_t)NSS_NOTFOUND);
393 
394 		ret = snprintf(userdata, sizeof (userdata),
395 		    _F_GETSERVBYPORT_SSD, strtol(portstr, (char **)NULL, 10));
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_GETSERVBYPORTPROTO,
404 		    strtol(portstr, (char **)NULL, 10), protocol);
405 		if (ret >= sizeof (searchfilter) || ret < 0)
406 			return ((nss_status_t)NSS_NOTFOUND);
407 
408 		ret = snprintf(userdata, sizeof (userdata),
409 		    _F_GETSERVBYPORTPROTO_SSD,
410 		    strtol(portstr, (char **)NULL, 10), protocol);
411 		if (ret >= sizeof (userdata) || ret < 0)
412 			return ((nss_status_t)NSS_NOTFOUND);
413 	}
414 
415 	return ((nss_status_t)_nss_ldap_lookup(be, argp,
416 		_SERVICES, searchfilter, NULL,
417 		_merge_SSD_filter, userdata));
418 }
419 
420 static ldap_backend_op_t serv_ops[] = {
421     _nss_ldap_destr,
422     _nss_ldap_endent,
423     _nss_ldap_setent,
424     _nss_ldap_getent,
425     getbyname,
426     getbyport
427 };
428 
429 
430 /*
431  * _nss_ldap_services_constr is where life begins. This function calls
432  * the generic ldap constructor function to define and build the
433  * abstract data types required to support ldap operations.
434  */
435 
436 /*ARGSUSED0*/
437 nss_backend_t *
438 _nss_ldap_services_constr(const char *dummy1, const char *dummy2,
439 			const char *dummy3)
440 {
441 
442 	return ((nss_backend_t *)_nss_ldap_constr(serv_ops,
443 		sizeof (serv_ops)/sizeof (serv_ops[0]), _SERVICES,
444 		services_attrs, _nss_ldap_services2str));
445 }
446