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
_nss_services_cookie_free(void ** ckP)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 *
_nss_services_cookie_new(ns_ldap_result_t * result,int index,char * cname)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
_nss_ldap_services2str(ldap_backend_ptr be,nss_XbyY_args_t * argp)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
getbyname(ldap_backend_ptr be,void * a)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
getbyport(ldap_backend_ptr be,void * a)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 *
_nss_ldap_services_constr(const char * dummy1,const char * dummy2,const char * dummy3)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