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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include "ldap_common.h"
26 #include <malloc.h>
27 #include <synch.h>
28 #include <syslog.h>
29 #include <rpcsvc/ypclnt.h>
30 #include <rpcsvc/yp_prot.h>
31 #include <thread.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <sys/stat.h>
36
37 /* getent attributes filters */
38 #define _F_GETALIASENT "(objectClass=rfc822MailGroup)"
39 #define _F_GETAUTHNAME "(objectClass=SolarisAuthAttr)"
40 #define _F_GETAUUSERNAME "(objectClass=SolarisAuditUser)"
41 #define _F_GETEXECNAME "(objectClass=SolarisExecAttr)"
42 #define _F_GETGRENT "(objectClass=posixGroup)"
43 #define _F_GETHOSTENT "(objectClass=ipHost)"
44 #define _F_GETNETENT "(objectClass=ipNetwork)"
45 #define _F_GETPROFNAME \
46 "(&(objectClass=SolarisProfAttr)(!(SolarisKernelSecurityPolicy=*)))"
47 #define _F_GETPROTOENT "(objectClass=ipProtocol)"
48 #define _F_GETPWENT "(objectClass=posixAccount)"
49 #define _F_GETPRINTERENT "(objectClass=sunPrinter)"
50 #define _F_GETRPCENT "(objectClass=oncRpc)"
51 #define _F_GETSERVENT "(objectClass=ipService)"
52 #define _F_GETSPENT "(objectclass=shadowAccount)"
53 #define _F_GETUSERNAME "(objectClass=SolarisUserAttr)"
54 #define _F_GETPROJENT "(objectClass=SolarisProject)"
55 #define _F_GETTNRHDB "(objectClass=ipTnetHost)"
56 #define _F_GETTNRHTP "(&(objectClass=ipTnetTemplate)"\
57 "(SolarisAttrKeyValue=*))"
58 #define _F_GETENT_SSD "(%s)"
59
60 /* getent sort attributes */
61 #define _A_UID "uid"
62 #define _A_GIDNUMBER "gidnumber"
63 #define _A_CN "cn"
64 #define _A_IPNETWORKNUM "ipnetworknumber"
65 #define _A_PROJECTNAM "SolarisProjectName"
66 #define _A_IPTNETNUM "ipTnetNumber"
67 #define _A_IPTNETTMPLNAM "ipTnetTemplateName"
68
69 static struct gettablefilter {
70 char *tablename;
71 char *tablefilter;
72 char *sortattr;
73 } gettablefilterent[] = {
74 {(char *)_PASSWD, (char *)_F_GETPWENT, (char *)_A_UID},
75 {(char *)_SHADOW, (char *)_F_GETSPENT, (char *)_A_UID},
76 {(char *)_GROUP, (char *)_F_GETGRENT, (char *)_A_GIDNUMBER},
77 {(char *)_HOSTS, (char *)_F_GETHOSTENT, (char *)_A_CN},
78 {(char *)_NETWORKS, (char *)_F_GETNETENT,
79 (char *)_A_IPNETWORKNUM},
80 {(char *)_PROTOCOLS, (char *)_F_GETPROTOENT, (char *)_A_CN},
81 {(char *)_RPC, (char *)_F_GETRPCENT, (char *)_A_CN},
82 {(char *)_ALIASES, (char *)_F_GETALIASENT, (char *)_A_CN},
83 {(char *)_SERVICES, (char *)_F_GETSERVENT, (char *)_A_CN},
84 {(char *)_AUUSER, (char *)_F_GETAUUSERNAME,
85 (char *)_A_UID},
86 {(char *)_AUTHATTR, (char *)_F_GETAUTHNAME, (char *)_A_CN},
87 {(char *)_EXECATTR, (char *)_F_GETEXECNAME, (char *)_A_CN},
88 {(char *)_PROFATTR, (char *)_F_GETPROFNAME, (char *)_A_CN},
89 {(char *)_USERATTR, (char *)_F_GETUSERNAME, (char *)_A_UID},
90 {(char *)_PROJECT, (char *)_F_GETPROJENT, (char *)_A_PROJECTNAM},
91 {(char *)_PRINTERS, (char *)_F_GETPRINTERENT, (char *)_A_CN},
92 {(char *)_TNRHDB, (char *)_F_GETTNRHDB, (char *)_A_IPTNETNUM},
93 {(char *)_TNRHTP, (char *)_F_GETTNRHTP,
94 (char *)_A_IPTNETTMPLNAM},
95 {(char *)NULL, (char *)NULL, (char *)NULL}
96 };
97
98
99 nss_status_t
switch_err(int rc,ns_ldap_error_t * error)100 switch_err(int rc, ns_ldap_error_t *error)
101 {
102 switch (rc) {
103 case NS_LDAP_SUCCESS:
104 return (NSS_SUCCESS);
105
106 case NS_LDAP_NOTFOUND:
107 return (NSS_NOTFOUND);
108
109 case NS_LDAP_PARTIAL:
110 return (NSS_TRYAGAIN);
111
112 case NS_LDAP_INTERNAL:
113 if (error && (error->status == LDAP_SERVER_DOWN ||
114 error->status == LDAP_TIMEOUT))
115 return (NSS_TRYAGAIN);
116 else
117 return (NSS_UNAVAIL);
118
119 default:
120 return (NSS_UNAVAIL);
121 }
122 }
123 /* ARGSUSED */
124 nss_status_t
_nss_ldap_lookup(ldap_backend_ptr be,nss_XbyY_args_t * argp,char * database,char * searchfilter,char * domain,int (* init_filter_cb)(const ns_ldap_search_desc_t * desc,char ** realfilter,const void * userdata),const void * userdata)125 _nss_ldap_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp,
126 char *database, char *searchfilter, char *domain,
127 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
128 char **realfilter, const void *userdata),
129 const void *userdata)
130 {
131 int callbackstat = 0;
132 ns_ldap_error_t *error = NULL;
133 int rc;
134
135 #ifdef DEBUG
136 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_lookup]\n");
137 (void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter);
138 (void) fprintf(stdout,
139 "\tuserdata: %s\n", userdata ? userdata : "NULL");
140 (void) fprintf(stdout, "\tdatabase: %s\n", database);
141 #endif /* DEBUG */
142
143 (void) __ns_ldap_freeResult(&be->result);
144
145 if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb,
146 be->attrs, NULL, 0, &be->result, &error, NULL,
147 userdata)) != NS_LDAP_SUCCESS) {
148 argp->returnval = 0;
149 rc = switch_err(rc, error);
150 (void) __ns_ldap_freeError(&error);
151
152 return (rc);
153 }
154 (void) __ns_ldap_freeError(&error);
155 /* callback function */
156 if ((callbackstat =
157 be->ldapobj2str(be, argp)) != NSS_STR_PARSE_SUCCESS) {
158 goto error_out;
159 }
160
161 /*
162 * publickey does not have a front end marshaller and expects
163 * a string to be returned in NSS.
164 * No need to convert file format -> struct.
165 *
166 */
167 if (be->db_type == NSS_LDAP_DB_PUBLICKEY) {
168 argp->returnval = argp->buf.buffer;
169 argp->returnlen = strlen(argp->buf.buffer);
170 be->db_type = NSS_LDAP_DB_NONE;
171 return (NSS_SUCCESS);
172 }
173 /*
174 * Assume the switch engine wants the returned data in the file
175 * format when argp->buf.result == NULL.
176 * The front-end marshaller str2ether(ethers) uses
177 * ent (argp->buf.result) and buffer (argp->buf.buffer)
178 * for different purpose so ethers has to be treated differently.
179 */
180 if (argp->buf.result != NULL ||
181 be->db_type == NSS_LDAP_DB_ETHERS) {
182 /* file format -> struct */
183 if (argp->str2ent == NULL) {
184 callbackstat = NSS_STR_PARSE_PARSE;
185 goto error_out;
186 }
187
188 callbackstat = (*argp->str2ent)(be->buffer,
189 be->buflen,
190 argp->buf.result,
191 argp->buf.buffer,
192 argp->buf.buflen);
193 if (callbackstat == NSS_STR_PARSE_SUCCESS) {
194 if (be->db_type == NSS_LDAP_DB_ETHERS &&
195 argp->buf.buffer != NULL) {
196 argp->returnval = argp->buf.buffer;
197 argp->returnlen = strlen(argp->buf.buffer);
198 } else {
199 argp->returnval = argp->buf.result;
200 argp->returnlen = 1; /* irrelevant */
201 }
202 if (be->buffer != NULL) {
203 free(be->buffer);
204 be->buffer = NULL;
205 be->buflen = 0;
206 be->db_type = NSS_LDAP_DB_NONE;
207 }
208 return ((nss_status_t)NSS_SUCCESS);
209 }
210 } else {
211 /* return file format in argp->buf.buffer */
212 argp->returnval = argp->buf.buffer;
213 argp->returnlen = strlen(argp->buf.buffer);
214 return ((nss_status_t)NSS_SUCCESS);
215 }
216
217 error_out:
218 if (be->buffer != NULL) {
219 free(be->buffer);
220 be->buffer = NULL;
221 be->buflen = 0;
222 be->db_type = NSS_LDAP_DB_NONE;
223 }
224 /* error */
225 if (callbackstat == NSS_STR_PARSE_PARSE) {
226 argp->returnval = 0;
227 return ((nss_status_t)NSS_NOTFOUND);
228 }
229 if (callbackstat == NSS_STR_PARSE_ERANGE) {
230 argp->erange = 1;
231 return ((nss_status_t)NSS_NOTFOUND);
232 }
233 if (callbackstat == NSS_STR_PARSE_NO_ADDR) {
234 /* No IPV4 address is found */
235 argp->h_errno = HOST_NOT_FOUND;
236 return ((nss_status_t)NSS_NOTFOUND);
237 }
238 return ((nss_status_t)NSS_UNAVAIL);
239 }
240
241 /*
242 * This function is similar to _nss_ldap_lookup except it does not
243 * do a callback. It is only used by getnetgrent.c
244 */
245
246 /* ARGSUSED */
247 nss_status_t
_nss_ldap_nocb_lookup(ldap_backend_ptr be,nss_XbyY_args_t * argp,char * database,char * searchfilter,char * domain,int (* init_filter_cb)(const ns_ldap_search_desc_t * desc,char ** realfilter,const void * userdata),const void * userdata)248 _nss_ldap_nocb_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp,
249 char *database, char *searchfilter, char *domain,
250 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
251 char **realfilter, const void *userdata),
252 const void *userdata)
253 {
254 ns_ldap_error_t *error = NULL;
255 int rc;
256
257 #ifdef DEBUG
258 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_nocb_lookup]\n");
259 (void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter);
260 (void) fprintf(stdout, "\tdatabase: %s\n", database);
261 (void) fprintf(stdout,
262 "\tuserdata: %s\n", userdata ? userdata : "NULL");
263 #endif /* DEBUG */
264
265 (void) __ns_ldap_freeResult(&be->result);
266
267 if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb,
268 be->attrs, NULL, 0, &be->result, &error, NULL,
269 userdata)) != NS_LDAP_SUCCESS) {
270 if (argp != NULL)
271 argp->returnval = 0;
272 rc = switch_err(rc, error);
273 (void) __ns_ldap_freeError(&error);
274 return (rc);
275 }
276
277 return ((nss_status_t)NSS_SUCCESS);
278 }
279
280
281 /*
282 *
283 */
284
285 void
_clean_ldap_backend(ldap_backend_ptr be)286 _clean_ldap_backend(ldap_backend_ptr be)
287 {
288 ns_ldap_error_t *error;
289
290 #ifdef DEBUG
291 (void) fprintf(stdout, "\n[ldap_common.c: _clean_ldap_backend]\n");
292 #endif /* DEBUG */
293
294 if (be->tablename != NULL)
295 free(be->tablename);
296 if (be->result != NULL)
297 (void) __ns_ldap_freeResult(&be->result);
298 if (be->enumcookie != NULL)
299 (void) __ns_ldap_endEntry(&be->enumcookie, &error);
300 if (be->services_cookie != NULL)
301 _nss_services_cookie_free((void **)&be->services_cookie);
302 if (be->toglue != NULL) {
303 free(be->toglue);
304 be->toglue = NULL;
305 }
306 if (be->buffer != NULL) {
307 free(be->buffer);
308 be->buffer = NULL;
309 }
310 free(be);
311 }
312
313
314 /*
315 * _nss_ldap_destr will free all smalloc'ed variable strings and structures
316 * before exiting this nsswitch shared backend library. This function is
317 * called before returning control back to nsswitch.
318 */
319
320 /*ARGSUSED1*/
321 nss_status_t
_nss_ldap_destr(ldap_backend_ptr be,void * a)322 _nss_ldap_destr(ldap_backend_ptr be, void *a)
323 {
324
325 #ifdef DEBUG
326 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_destr]\n");
327 #endif /* DEBUG */
328
329 (void) _clean_ldap_backend(be);
330
331 return ((nss_status_t)NSS_SUCCESS);
332 }
333
334
335 /*
336 * _nss_ldap_setent called before _nss_ldap_getent. This function is
337 * required by POSIX.
338 */
339
340 nss_status_t
_nss_ldap_setent(ldap_backend_ptr be,void * a)341 _nss_ldap_setent(ldap_backend_ptr be, void *a)
342 {
343 struct gettablefilter *gtf;
344
345 #ifdef DEBUG
346 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_setent]\n");
347 #endif /* DEBUG */
348
349 if (be->setcalled == 1)
350 (void) _nss_ldap_endent(be, a);
351 be->filter = NULL;
352 be->sortattr = NULL;
353 for (gtf = gettablefilterent; gtf->tablename != (char *)NULL; gtf++) {
354 if (strcmp(gtf->tablename, be->tablename))
355 continue;
356 be->filter = (char *)gtf->tablefilter;
357 be->sortattr = (char *)gtf->sortattr;
358 break;
359 }
360
361 be->setcalled = 1;
362 be->enumcookie = NULL;
363 be->result = NULL;
364 be->services_cookie = NULL;
365 be->buffer = NULL;
366 return ((nss_status_t)NSS_SUCCESS);
367 }
368
369
370 /*
371 * _nss_ldap_endent called after _nss_ldap_getent. This function is
372 * required by POSIX.
373 */
374
375 /*ARGSUSED1*/
376 nss_status_t
_nss_ldap_endent(ldap_backend_ptr be,void * a)377 _nss_ldap_endent(ldap_backend_ptr be, void *a)
378 {
379 ns_ldap_error_t *error = NULL;
380
381 #ifdef DEBUG
382 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_endent]\n");
383 #endif /* DEBUG */
384
385 be->setcalled = 0;
386 be->filter = NULL;
387 be->sortattr = NULL;
388 if (be->enumcookie != NULL) {
389 (void) __ns_ldap_endEntry(&be->enumcookie, &error);
390 (void) __ns_ldap_freeError(&error);
391 }
392 if (be->result != NULL) {
393 (void) __ns_ldap_freeResult(&be->result);
394 }
395 if (be->services_cookie != NULL) {
396 _nss_services_cookie_free((void **)&be->services_cookie);
397 }
398 if (be->buffer != NULL) {
399 free(be->buffer);
400 be->buffer = NULL;
401 }
402
403 return ((nss_status_t)NSS_SUCCESS);
404 }
405
406
407 /*
408 *
409 */
410
411 nss_status_t
_nss_ldap_getent(ldap_backend_ptr be,void * a)412 _nss_ldap_getent(ldap_backend_ptr be, void *a)
413 {
414 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
415 ns_ldap_error_t *error = NULL;
416 int parsestat = 0;
417 int retcode = 0;
418
419 #ifdef DEBUG
420 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_getent]\n");
421 #endif /* DEBUG */
422
423 if (be->setcalled == 0)
424 (void) _nss_ldap_setent(be, a);
425
426 next_entry:
427 if (be->enumcookie == NULL) {
428 retcode = __ns_ldap_firstEntry(be->tablename,
429 be->filter, be->sortattr, _merge_SSD_filter, be->attrs,
430 NULL, 0, &be->enumcookie,
431 &be->result, &error, _F_GETENT_SSD);
432 } else {
433 if (be->services_cookie == NULL) {
434 retcode = __ns_ldap_nextEntry(be->enumcookie,
435 &be->result, &error);
436 }
437 }
438 if (retcode != NS_LDAP_SUCCESS) {
439 retcode = switch_err(retcode, error);
440 (void) __ns_ldap_freeError(&error);
441 (void) _nss_ldap_endent(be, a);
442 return (retcode);
443 }
444
445 if (be->result == NULL) {
446 parsestat = NSS_STR_PARSE_NO_RESULT;
447 goto error_out;
448 }
449 /* ns_ldap_entry_t -> file format */
450 if ((parsestat = be->ldapobj2str(be, argp))
451 == NSS_STR_PARSE_SUCCESS) {
452 if (argp->buf.result != NULL) {
453 /* file format -> struct */
454 if (argp->str2ent == NULL) {
455 parsestat = NSS_STR_PARSE_NO_RESULT;
456 goto error_out;
457 }
458 parsestat = (*argp->str2ent)(be->buffer,
459 be->buflen,
460 argp->buf.result,
461 argp->buf.buffer,
462 argp->buf.buflen);
463 if (parsestat == NSS_STR_PARSE_SUCCESS) {
464 if (be->buffer != NULL) {
465 free(be->buffer);
466 be->buffer = NULL;
467 be->buflen = 0;
468 }
469 be->result = NULL;
470 argp->returnval = argp->buf.result;
471 argp->returnlen = 1; /* irrevelant */
472 return ((nss_status_t)NSS_SUCCESS);
473 }
474 } else {
475 /*
476 * nscd is not caching the enumerated
477 * entries. This code path would be dormant.
478 * Keep this path for the future references.
479 */
480 argp->returnval = argp->buf.buffer;
481 argp->returnlen =
482 strlen(argp->buf.buffer) + 1;
483 }
484 }
485 error_out:
486 if (be->buffer != NULL) {
487 free(be->buffer);
488 be->buffer = NULL;
489 be->buflen = 0;
490 }
491 be->result = NULL;
492 if (parsestat == NSS_STR_PARSE_NO_RESULT) {
493 argp->returnval = 0;
494 (void) _nss_ldap_endent(be, a);
495 return ((nss_status_t)NSS_NOTFOUND);
496 }
497
498 if (parsestat == NSS_STR_PARSE_ERANGE) {
499 argp->erange = 1;
500 (void) _nss_ldap_endent(be, a);
501 return ((nss_status_t)NSS_NOTFOUND);
502 }
503 if (parsestat == NSS_STR_PARSE_NO_ADDR)
504 /*
505 * No IPV4 address is found in the current entry.
506 * It indicates that the entry contains IPV6 addresses
507 * only. Instead of calling _nss_ldap_endent to
508 * terminate, get next entry to continue enumeration.
509 * If it returned NSS_NOTFOUND here,
510 * gethostent() would return NULL
511 * and the enumeration would stop prematurely.
512 */
513 goto next_entry;
514
515 if (parsestat == NSS_STR_PARSE_PARSE)
516 /*
517 * There has been a parse error. Most likely some
518 * mandatory attributes are missing. Ignore the error
519 * and get the next entry. If we returned an error the
520 * enumeration would stop prematurely.
521 */
522 goto next_entry;
523
524 return ((nss_status_t)NSS_SUCCESS);
525 }
526
527
528 /*
529 *
530 */
531
532 nss_backend_t *
_nss_ldap_constr(ldap_backend_op_t ops[],int nops,char * tablename,const char ** attrs,fnf ldapobj2str)533 _nss_ldap_constr(ldap_backend_op_t ops[], int nops, char *tablename,
534 const char **attrs, fnf ldapobj2str)
535 {
536 ldap_backend_ptr be;
537
538 #ifdef DEBUG
539 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_constr]\n");
540 #endif /* DEBUG */
541
542 if ((be = (ldap_backend_ptr) calloc(1, sizeof (*be))) == 0)
543 return (0);
544 be->ops = ops;
545 be->nops = (nss_dbop_t)nops;
546 be->tablename = (char *)strdup(tablename);
547 be->attrs = attrs;
548 be->ldapobj2str = ldapobj2str;
549
550 return ((nss_backend_t *)be);
551 }
552
553
554 /*
555 *
556 */
557 int
chophostdomain(char * string,char * host,char * domain)558 chophostdomain(char *string, char *host, char *domain)
559 {
560 char *dot;
561
562 if (string == NULL)
563 return (-1);
564
565 if ((dot = strchr(string, '.')) == NULL) {
566 return (0);
567 }
568 *dot = '\0';
569 (void) strcpy(host, string);
570 (void) strcpy(domain, ++dot);
571
572 return (0);
573 }
574
575
576 /*
577 *
578 */
579 int
propersubdomain(char * domain,char * subdomain)580 propersubdomain(char *domain, char *subdomain)
581 {
582 int domainlen, subdomainlen;
583
584 /* sanity check */
585 if (domain == NULL || subdomain == NULL)
586 return (-1);
587
588 domainlen = strlen(domain);
589 subdomainlen = strlen(subdomain);
590
591 /* is afterdot a substring of domain? */
592 if ((strncasecmp(domain, subdomain, subdomainlen)) != 0)
593 return (-1);
594
595 if (domainlen == subdomainlen)
596 return (1);
597
598 if (subdomainlen > domainlen)
599 return (-1);
600
601 if (*(domain + subdomainlen) != '.')
602 return (-1);
603
604 return (1);
605 }
606