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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
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 <pthread.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <signal.h>
36 #include <sys/stat.h>
37 #include <assert.h>
38 #include "ad_common.h"
39
40 static pthread_mutex_t statelock = PTHREAD_MUTEX_INITIALIZER;
41 static nssad_state_t state = {0};
42
43 static void
nssad_cfg_free_props(nssad_prop_t * props)44 nssad_cfg_free_props(nssad_prop_t *props)
45 {
46 if (props->domain_name != NULL) {
47 free(props->domain_name);
48 props->domain_name = NULL;
49 }
50 if (props->domain_controller != NULL) {
51 free(props->domain_controller);
52 props->domain_controller = NULL;
53 }
54 }
55
56 static int
nssad_cfg_discover_props(const char * domain,ad_disc_t ad_ctx,nssad_prop_t * props)57 nssad_cfg_discover_props(const char *domain, ad_disc_t ad_ctx,
58 nssad_prop_t *props)
59 {
60 ad_disc_refresh(ad_ctx);
61 if (ad_disc_set_DomainName(ad_ctx, domain) != 0)
62 return (-1);
63 if (props->domain_controller == NULL)
64 props->domain_controller =
65 ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE,
66 NULL);
67 return (0);
68 }
69
70 static int
nssad_cfg_reload_ad(nssad_prop_t * props,adutils_ad_t ** ad)71 nssad_cfg_reload_ad(nssad_prop_t *props, adutils_ad_t **ad)
72 {
73 int i;
74 adutils_ad_t *new;
75
76 if (props->domain_controller == NULL ||
77 props->domain_controller[0].host[0] == '\0')
78 return (0);
79 if (adutils_ad_alloc(&new, props->domain_name,
80 ADUTILS_AD_DATA) != ADUTILS_SUCCESS)
81 return (-1);
82 for (i = 0; props->domain_controller[i].host[0] != '\0'; i++) {
83 if (adutils_add_ds(new,
84 props->domain_controller[i].host,
85 props->domain_controller[i].port) != ADUTILS_SUCCESS) {
86 adutils_ad_free(&new);
87 return (-1);
88 }
89 }
90
91 if (*ad != NULL)
92 adutils_ad_free(ad);
93 *ad = new;
94 return (0);
95 }
96
97 static
98 int
update_dirs(idmap_ad_disc_ds_t ** value,idmap_ad_disc_ds_t ** new)99 update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new)
100 {
101 if (*value == *new)
102 return (0);
103
104 if (*value != NULL && *new != NULL &&
105 ad_disc_compare_ds(*value, *new) == 0) {
106 free(*new);
107 *new = NULL;
108 return (0);
109 }
110
111 if (*value)
112 free(*value);
113 *value = *new;
114 *new = NULL;
115 return (1);
116 }
117
118 static
119 int
nssad_cfg_refresh(nssad_cfg_t * cp)120 nssad_cfg_refresh(nssad_cfg_t *cp)
121 {
122 nssad_prop_t props;
123
124 (void) ad_disc_SubnetChanged(cp->ad_ctx);
125 (void) memset(&props, 0, sizeof (props));
126 if (nssad_cfg_discover_props(cp->props.domain_name, cp->ad_ctx,
127 &props) < 0)
128 return (-1);
129 if (update_dirs(&cp->props.domain_controller,
130 &props.domain_controller)) {
131 if (cp->props.domain_controller != NULL &&
132 cp->props.domain_controller[0].host[0] != '\0')
133 (void) nssad_cfg_reload_ad(&cp->props, &cp->ad);
134 }
135 return (0);
136 }
137
138 static void
nssad_cfg_destroy(nssad_cfg_t * cp)139 nssad_cfg_destroy(nssad_cfg_t *cp)
140 {
141 if (cp != NULL) {
142 (void) pthread_rwlock_destroy(&cp->lock);
143 ad_disc_fini(cp->ad_ctx);
144 nssad_cfg_free_props(&cp->props);
145 adutils_ad_free(&cp->ad);
146 free(cp);
147 }
148 }
149
150 static nssad_cfg_t *
nssad_cfg_create(const char * domain)151 nssad_cfg_create(const char *domain)
152 {
153 nssad_cfg_t *cp;
154
155 if ((cp = calloc(1, sizeof (*cp))) == NULL)
156 return (NULL);
157 if (pthread_rwlock_init(&cp->lock, NULL) != 0) {
158 free(cp);
159 return (NULL);
160 }
161 if ((cp->ad_ctx = ad_disc_init()) == NULL)
162 goto errout;
163 if ((cp->props.domain_name = strdup(domain)) == NULL)
164 goto errout;
165 if (nssad_cfg_discover_props(domain, cp->ad_ctx, &cp->props) < 0)
166 goto errout;
167 if (nssad_cfg_reload_ad(&cp->props, &cp->ad) < 0)
168 goto errout;
169 return (cp);
170 errout:
171 nssad_cfg_destroy(cp);
172 return (NULL);
173 }
174
175 #define hex_char(n) "0123456789abcdef"[n & 0xf]
176
177 int
_ldap_filter_name(char * filter_name,const char * name,int filter_name_size)178 _ldap_filter_name(char *filter_name, const char *name, int filter_name_size)
179 {
180 char *end = filter_name + filter_name_size;
181 char c;
182
183 for (; *name; name++) {
184 c = *name;
185 switch (c) {
186 case '*':
187 case '(':
188 case ')':
189 case '\\':
190 if (end <= filter_name + 3)
191 return (-1);
192 *filter_name++ = '\\';
193 *filter_name++ = hex_char(c >> 4);
194 *filter_name++ = hex_char(c & 0xf);
195 break;
196 default:
197 if (end <= filter_name + 1)
198 return (-1);
199 *filter_name++ = c;
200 break;
201 }
202 }
203 if (end <= filter_name)
204 return (-1);
205 *filter_name = '\0';
206 return (0);
207 }
208
209 static
210 nss_status_t
map_adrc2nssrc(adutils_rc adrc)211 map_adrc2nssrc(adutils_rc adrc)
212 {
213 if (adrc == ADUTILS_SUCCESS)
214 return ((nss_status_t)NSS_SUCCESS);
215 if (adrc == ADUTILS_ERR_NOTFOUND)
216 errno = 0;
217 return ((nss_status_t)NSS_NOTFOUND);
218 }
219
220 /* ARGSUSED */
221 nss_status_t
_nss_ad_marshall_data(ad_backend_ptr be,nss_XbyY_args_t * argp)222 _nss_ad_marshall_data(ad_backend_ptr be, nss_XbyY_args_t *argp)
223 {
224 int stat;
225
226 if (argp->buf.result == NULL) {
227 /*
228 * This suggests that the process (e.g. nscd) expects
229 * nssad to return the data in native file format in
230 * argp->buf.buffer i.e. no need to marshall the data.
231 */
232 argp->returnval = argp->buf.buffer;
233 argp->returnlen = strlen(argp->buf.buffer);
234 return ((nss_status_t)NSS_STR_PARSE_SUCCESS);
235 }
236
237 if (argp->str2ent == NULL)
238 return ((nss_status_t)NSS_STR_PARSE_PARSE);
239
240 stat = (*argp->str2ent)(be->buffer, be->buflen,
241 argp->buf.result, argp->buf.buffer, argp->buf.buflen);
242
243 if (stat == NSS_STR_PARSE_SUCCESS) {
244 argp->returnval = argp->buf.result;
245 argp->returnlen = 1; /* irrelevant */
246 }
247 return ((nss_status_t)stat);
248 }
249
250 nss_status_t
_nss_ad_sanitize_status(ad_backend_ptr be,nss_XbyY_args_t * argp,nss_status_t stat)251 _nss_ad_sanitize_status(ad_backend_ptr be, nss_XbyY_args_t *argp,
252 nss_status_t stat)
253 {
254 if (be->buffer != NULL) {
255 free(be->buffer);
256 be->buffer = NULL;
257 be->buflen = 0;
258 be->db_type = NSS_AD_DB_NONE;
259 }
260
261 if (stat == NSS_STR_PARSE_SUCCESS) {
262 return ((nss_status_t)NSS_SUCCESS);
263 } else if (stat == NSS_STR_PARSE_PARSE) {
264 argp->returnval = 0;
265 return ((nss_status_t)NSS_NOTFOUND);
266 } else if (stat == NSS_STR_PARSE_ERANGE) {
267 argp->erange = 1;
268 return ((nss_status_t)NSS_NOTFOUND);
269 }
270 return ((nss_status_t)NSS_UNAVAIL);
271 }
272
273 /* ARGSUSED */
274 static
275 nssad_cfg_t *
get_cfg(const char * domain)276 get_cfg(const char *domain)
277 {
278 nssad_cfg_t *cp, *lru, *prev;
279
280 /*
281 * Note about the queue:
282 *
283 * The queue is used to hold our per domain
284 * configs. The queue is limited to CFG_QUEUE_MAX_SIZE.
285 * If the queue increases beyond that point we toss
286 * out the LRU entry. The entries are inserted into
287 * the queue at state.qtail and the LRU entry is
288 * removed from state.qhead. state.qnext points
289 * from the qtail to the qhead. Everytime a config
290 * is accessed it is moved to qtail.
291 */
292
293 (void) pthread_mutex_lock(&statelock);
294
295 for (cp = state.qtail, prev = NULL; cp != NULL;
296 prev = cp, cp = cp->qnext) {
297 if (cp->props.domain_name == NULL ||
298 strcasecmp(cp->props.domain_name, domain) != 0)
299 continue;
300
301 /* Found config for the given domain. */
302
303 if (state.qtail != cp) {
304 /*
305 * Move the entry to the tail of the queue.
306 * This way the LRU entry can be found at
307 * the head of the queue.
308 */
309 prev->qnext = cp->qnext;
310 if (state.qhead == cp)
311 state.qhead = prev;
312 cp->qnext = state.qtail;
313 state.qtail = cp;
314 }
315
316 if (ad_disc_get_TTL(cp->ad_ctx) == 0) {
317 /*
318 * If there are expired items in the
319 * config, grab the write lock and
320 * refresh the config.
321 */
322 (void) pthread_rwlock_wrlock(&cp->lock);
323 if (nssad_cfg_refresh(cp) < 0) {
324 (void) pthread_rwlock_unlock(&cp->lock);
325 (void) pthread_mutex_unlock(&statelock);
326 return (NULL);
327 }
328 (void) pthread_rwlock_unlock(&cp->lock);
329 }
330
331 /* Return the config found */
332 (void) pthread_rwlock_rdlock(&cp->lock);
333 (void) pthread_mutex_unlock(&statelock);
334 return (cp);
335 }
336
337 /* Create new config entry for the domain */
338 if ((cp = nssad_cfg_create(domain)) == NULL) {
339 (void) pthread_mutex_unlock(&statelock);
340 return (NULL);
341 }
342
343 /* Add it to the queue */
344 state.qcount++;
345 if (state.qtail == NULL) {
346 state.qtail = state.qhead = cp;
347 (void) pthread_rwlock_rdlock(&cp->lock);
348 (void) pthread_mutex_unlock(&statelock);
349 return (cp);
350 }
351 cp->qnext = state.qtail;
352 state.qtail = cp;
353
354 /* If the queue has exceeded its size, remove the LRU entry */
355 if (state.qcount >= CFG_QUEUE_MAX_SIZE) {
356 /* Detach the lru entry and destroy */
357 lru = state.qhead;
358 if (pthread_rwlock_trywrlock(&lru->lock) == 0) {
359 for (prev = state.qtail; prev != NULL;
360 prev = prev->qnext) {
361 if (prev->qnext != lru)
362 continue;
363 state.qhead = prev;
364 prev->qnext = NULL;
365 state.qcount--;
366 (void) pthread_rwlock_unlock(&lru->lock);
367 nssad_cfg_destroy(lru);
368 break;
369 }
370 (void) assert(prev != NULL);
371 }
372 }
373
374 (void) pthread_rwlock_rdlock(&cp->lock);
375 (void) pthread_mutex_unlock(&statelock);
376 return (cp);
377 }
378
379
380 /* ARGSUSED */
381 static
382 nss_status_t
ad_lookup(const char * filter,const char ** attrs,const char * domain,adutils_result_t ** result)383 ad_lookup(const char *filter, const char **attrs,
384 const char *domain, adutils_result_t **result)
385 {
386 int retries = 0;
387 adutils_rc rc, brc;
388 adutils_query_state_t *qs;
389 nssad_cfg_t *cp;
390
391 retry:
392 if ((cp = get_cfg(domain)) == NULL)
393 return ((nss_status_t)NSS_NOTFOUND);
394
395 rc = adutils_lookup_batch_start(cp->ad, 1, NULL, NULL, &qs);
396 (void) pthread_rwlock_unlock(&cp->lock);
397 if (rc != ADUTILS_SUCCESS)
398 goto out;
399
400 rc = adutils_lookup_batch_add(qs, filter, attrs, domain, result, &brc);
401 if (rc != ADUTILS_SUCCESS) {
402 adutils_lookup_batch_release(&qs);
403 goto out;
404 }
405
406 rc = adutils_lookup_batch_end(&qs);
407 if (rc != ADUTILS_SUCCESS)
408 goto out;
409 rc = brc;
410
411 out:
412 if (rc == ADUTILS_ERR_RETRIABLE_NET_ERR &&
413 retries++ < ADUTILS_DEF_NUM_RETRIES)
414 goto retry;
415 return (map_adrc2nssrc(rc));
416 }
417
418
419 /* ARGSUSED */
420 nss_status_t
_nss_ad_lookup(ad_backend_ptr be,nss_XbyY_args_t * argp,const char * database,const char * searchfilter,const char * dname,int * try_idmap)421 _nss_ad_lookup(ad_backend_ptr be, nss_XbyY_args_t *argp,
422 const char *database, const char *searchfilter,
423 const char *dname, int *try_idmap)
424 {
425 nss_status_t stat;
426
427 *try_idmap = 0;
428
429 /* Clear up results if any */
430 (void) adutils_freeresult(&be->result);
431
432 /* Lookup AD */
433 stat = ad_lookup(searchfilter, be->attrs, dname, &be->result);
434 if (stat != NSS_SUCCESS) {
435 argp->returnval = 0;
436 *try_idmap = 1;
437 return (stat);
438 }
439
440 /* Map AD object(s) to string in native file format */
441 stat = be->adobj2str(be, argp);
442 if (stat == NSS_STR_PARSE_SUCCESS)
443 stat = _nss_ad_marshall_data(be, argp);
444 return (_nss_ad_sanitize_status(be, argp, stat));
445 }
446
447 static
448 void
clean_state()449 clean_state()
450 {
451 nssad_cfg_t *cp, *curr;
452
453 (void) pthread_mutex_lock(&statelock);
454 for (cp = state.qtail; cp != NULL; ) {
455 curr = cp;
456 cp = cp->qnext;
457 nssad_cfg_destroy(curr);
458 }
459 (void) memset(&state, 0, sizeof (state));
460 (void) pthread_mutex_unlock(&statelock);
461 }
462
463 static
464 void
_clean_ad_backend(ad_backend_ptr be)465 _clean_ad_backend(ad_backend_ptr be)
466 {
467 if (be->tablename != NULL)
468 free(be->tablename);
469 if (be->buffer != NULL) {
470 free(be->buffer);
471 be->buffer = NULL;
472 }
473 free(be);
474 }
475
476
477 /*
478 * _nss_ad_destr frees allocated memory before exiting this nsswitch shared
479 * backend library. This function is called before returning control back to
480 * nsswitch.
481 */
482 /*ARGSUSED*/
483 nss_status_t
_nss_ad_destr(ad_backend_ptr be,void * a)484 _nss_ad_destr(ad_backend_ptr be, void *a)
485 {
486 (void) _clean_ad_backend(be);
487 clean_state();
488 return ((nss_status_t)NSS_SUCCESS);
489 }
490
491
492 /*ARGSUSED*/
493 nss_status_t
_nss_ad_setent(ad_backend_ptr be,void * a)494 _nss_ad_setent(ad_backend_ptr be, void *a)
495 {
496 return ((nss_status_t)NSS_UNAVAIL);
497 }
498
499
500 /*ARGSUSED*/
501 nss_status_t
_nss_ad_endent(ad_backend_ptr be,void * a)502 _nss_ad_endent(ad_backend_ptr be, void *a)
503 {
504 return ((nss_status_t)NSS_UNAVAIL);
505 }
506
507
508 /*ARGSUSED*/
509 nss_status_t
_nss_ad_getent(ad_backend_ptr be,void * a)510 _nss_ad_getent(ad_backend_ptr be, void *a)
511 {
512 return ((nss_status_t)NSS_UNAVAIL);
513 }
514
515
516 nss_backend_t *
_nss_ad_constr(ad_backend_op_t ops[],int nops,char * tablename,const char ** attrs,fnf adobj2str)517 _nss_ad_constr(ad_backend_op_t ops[], int nops, char *tablename,
518 const char **attrs, fnf adobj2str)
519 {
520 ad_backend_ptr be;
521
522 if ((be = (ad_backend_ptr) calloc(1, sizeof (*be))) == NULL)
523 return (NULL);
524 if ((be->tablename = (char *)strdup(tablename)) == NULL) {
525 free(be);
526 return (NULL);
527 }
528 be->ops = ops;
529 be->nops = (nss_dbop_t)nops;
530 be->attrs = attrs;
531 be->adobj2str = adobj2str;
532 (void) memset(&state, 0, sizeof (state));
533 return ((nss_backend_t *)be);
534 }
535