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