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