Line data Source code
1 : /*
2 : SSSD
3 :
4 : Authors:
5 : Stephen Gallagher <sgallagh@redhat.com>
6 :
7 : Copyright (C) 2012 Red Hat
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 :
24 : #include <sys/types.h>
25 : #include <unistd.h>
26 : #include <sys/stat.h>
27 : #include <fcntl.h>
28 :
29 : #include <sasl/sasl.h>
30 :
31 : #include "util/util.h"
32 : #include "providers/ad/ad_common.h"
33 : #include "providers/ad/ad_access.h"
34 : #include "providers/ldap/ldap_common.h"
35 : #include "providers/ldap/sdap_access.h"
36 : #include "providers/ldap/sdap_idmap.h"
37 : #include "providers/krb5/krb5_auth.h"
38 : #include "providers/krb5/krb5_init_shared.h"
39 : #include "providers/ad/ad_id.h"
40 : #include "providers/ad/ad_srv.h"
41 : #include "providers/dp_dyndns.h"
42 : #include "providers/ad/ad_subdomains.h"
43 : #include "providers/ad/ad_domain_info.h"
44 :
45 : struct ad_options *ad_options = NULL;
46 :
47 : static void
48 : ad_shutdown(struct be_req *req);
49 :
50 : struct bet_ops ad_id_ops = {
51 : .handler = ad_account_info_handler,
52 : .finalize = ad_shutdown,
53 : .check_online = ad_check_online
54 : };
55 :
56 : struct bet_ops ad_auth_ops = {
57 : .handler = krb5_pam_handler,
58 : .finalize = NULL
59 : };
60 :
61 : struct bet_ops ad_chpass_ops = {
62 : .handler = krb5_pam_handler,
63 : .finalize = NULL
64 : };
65 :
66 : struct bet_ops ad_access_ops = {
67 : .handler = ad_access_handler,
68 : .finalize = NULL
69 : };
70 :
71 : #define AD_COMPAT_ON "1"
72 0 : static int ad_sasl_getopt(void *context, const char *plugin_name,
73 : const char *option,
74 : const char **result, unsigned *len)
75 : {
76 0 : if (!plugin_name || !result) {
77 0 : return SASL_FAIL;
78 : }
79 0 : if (strcmp(plugin_name, "GSSAPI") != 0) {
80 0 : return SASL_FAIL;
81 : }
82 0 : if (strcmp(option, "ad_compat") != 0) {
83 0 : return SASL_FAIL;
84 : }
85 0 : *result = AD_COMPAT_ON;
86 0 : if (len) {
87 0 : *len = 2;
88 : }
89 0 : return SASL_OK;
90 : }
91 :
92 : typedef int (*sss_sasl_gen_cb_fn)(void);
93 :
94 0 : static int map_sasl2sssd_log_level(int sasl_level)
95 : {
96 : int sssd_level;
97 :
98 0 : switch(sasl_level) {
99 : case SASL_LOG_ERR: /* log unusual errors (default) */
100 0 : sssd_level = SSSDBG_CRIT_FAILURE;
101 0 : break;
102 : case SASL_LOG_FAIL: /* log all authentication failures */
103 0 : sssd_level = SSSDBG_OP_FAILURE;
104 0 : break;
105 : case SASL_LOG_WARN: /* log non-fatal warnings */
106 0 : sssd_level = SSSDBG_MINOR_FAILURE;
107 0 : break;
108 : case SASL_LOG_NOTE: /* more verbose than LOG_WARN */
109 : case SASL_LOG_DEBUG: /* more verbose than LOG_NOTE */
110 : case SASL_LOG_TRACE: /* traces of internal protocols */
111 : case SASL_LOG_PASS: /* traces of internal protocols, including */
112 0 : sssd_level = SSSDBG_TRACE_ALL;
113 0 : break;
114 : default:
115 0 : sssd_level = SSSDBG_TRACE_ALL;
116 0 : break;
117 : }
118 :
119 0 : return sssd_level;
120 : }
121 :
122 0 : int ad_sasl_log(void *context, int level, const char *message)
123 : {
124 : int sssd_level;
125 :
126 0 : if (level == SASL_LOG_ERR || level == SASL_LOG_FAIL) {
127 0 : sss_log(SSS_LOG_ERR, "%s\n", message);
128 : }
129 :
130 0 : sssd_level = map_sasl2sssd_log_level(level);
131 0 : DEBUG(sssd_level, "SASL: %s\n", message);
132 0 : return SASL_OK;
133 : }
134 :
135 : static const sasl_callback_t ad_sasl_callbacks[] = {
136 : { SASL_CB_GETOPT, (sss_sasl_gen_cb_fn)ad_sasl_getopt, NULL },
137 : { SASL_CB_LOG, (sss_sasl_gen_cb_fn)ad_sasl_log, NULL },
138 : { SASL_CB_LIST_END, NULL, NULL }
139 : };
140 : /* This is quite a hack, we *try* to fool openldap libraries by initializing
141 : * sasl first so we can pass in the SASL_CB_GETOPT callback we need to set some
142 : * options. Should be removed as soon as openldap exposes a way to do that */
143 0 : static void ad_sasl_initialize(void)
144 : {
145 : /* NOTE: this may fail if soe other library in the system happens to
146 : * initialize and use openldap libraries or directly the cyrus-sasl
147 : * library as this initialization function can be called only once per
148 : * process */
149 0 : (void)sasl_client_init(ad_sasl_callbacks);
150 0 : }
151 :
152 : static errno_t
153 0 : common_ad_init(struct be_ctx *bectx)
154 : {
155 : errno_t ret;
156 0 : char *ad_servers = NULL;
157 0 : char *ad_backup_servers = NULL;
158 : char *ad_realm;
159 :
160 0 : ad_sasl_initialize();
161 :
162 : /* Get AD-specific options */
163 0 : ret = ad_get_common_options(bectx, bectx->cdb,
164 : bectx->conf_path,
165 : bectx->domain,
166 : &ad_options);
167 0 : if (ret != EOK) {
168 0 : DEBUG(SSSDBG_FATAL_FAILURE,
169 : "Could not parse common options: [%s]\n",
170 : strerror(ret));
171 0 : goto done;
172 : }
173 :
174 0 : ad_servers = dp_opt_get_string(ad_options->basic, AD_SERVER);
175 0 : ad_backup_servers = dp_opt_get_string(ad_options->basic, AD_BACKUP_SERVER);
176 0 : ad_realm = dp_opt_get_string(ad_options->basic, AD_KRB5_REALM);
177 :
178 : /* Set up the failover service */
179 0 : ret = ad_failover_init(ad_options, bectx, ad_servers, ad_backup_servers, ad_realm,
180 : AD_SERVICE_NAME, AD_GC_SERVICE_NAME,
181 0 : dp_opt_get_string(ad_options->basic, AD_DOMAIN),
182 0 : &ad_options->service);
183 0 : if (ret != EOK) {
184 0 : DEBUG(SSSDBG_FATAL_FAILURE,
185 : "Failed to init AD failover service: [%s]\n",
186 : strerror(ret));
187 0 : goto done;
188 : }
189 :
190 0 : ret = EOK;
191 : done:
192 0 : return ret;
193 : }
194 :
195 : int
196 0 : sssm_ad_id_init(struct be_ctx *bectx,
197 : struct bet_ops **ops,
198 : void **pvt_data)
199 : {
200 : errno_t ret;
201 : struct ad_id_ctx *ad_ctx;
202 : const char *hostname;
203 : const char *ad_domain;
204 : const char *ad_site_override;
205 : struct ad_srv_plugin_ctx *srv_ctx;
206 :
207 0 : if (!ad_options) {
208 0 : ret = common_ad_init(bectx);
209 0 : if (ret != EOK) {
210 0 : return ret;
211 : }
212 : }
213 :
214 0 : if (ad_options->id_ctx) {
215 : /* already initialized */
216 0 : *ops = &ad_id_ops;
217 0 : *pvt_data = ad_options->id_ctx;
218 0 : return EOK;
219 : }
220 :
221 :
222 0 : ad_ctx = ad_id_ctx_init(ad_options, bectx);
223 0 : if (ad_ctx == NULL) {
224 0 : return ENOMEM;
225 : }
226 0 : ad_options->id_ctx = ad_ctx;
227 :
228 0 : ret = ad_dyndns_init(ad_ctx->sdap_id_ctx->be, ad_options);
229 0 : if (ret != EOK) {
230 0 : DEBUG(SSSDBG_MINOR_FAILURE,
231 : "Failure setting up automatic DNS update\n");
232 : /* Continue without DNS updates */
233 : }
234 :
235 0 : ret = sdap_setup_child();
236 0 : if (ret != EOK) {
237 0 : DEBUG(SSSDBG_FATAL_FAILURE,
238 : "setup_child failed [%d][%s].\n",
239 : ret, strerror(ret));
240 0 : goto done;
241 : }
242 :
243 : /* Set up various SDAP options */
244 0 : ret = ad_get_id_options(ad_options, bectx->cdb,
245 : bectx->conf_path,
246 0 : &ad_ctx->sdap_id_ctx->opts);
247 0 : if (ret != EOK) {
248 0 : goto done;
249 : }
250 :
251 0 : ret = sdap_id_setup_tasks(bectx,
252 : ad_ctx->sdap_id_ctx,
253 0 : ad_ctx->sdap_id_ctx->opts->sdom,
254 : ad_enumeration_send,
255 : ad_enumeration_recv,
256 : ad_ctx);
257 0 : if (ret != EOK) {
258 0 : goto done;
259 : }
260 :
261 0 : ad_ctx->sdap_id_ctx->opts->sdom->pvt = ad_ctx;
262 :
263 : /* Set up the ID mapping object */
264 0 : ret = sdap_idmap_init(ad_ctx->sdap_id_ctx, ad_ctx->sdap_id_ctx,
265 0 : &ad_ctx->sdap_id_ctx->opts->idmap_ctx);
266 0 : if (ret != EOK) goto done;
267 :
268 0 : ret = setup_tls_config(ad_ctx->sdap_id_ctx->opts->basic);
269 0 : if (ret != EOK) {
270 0 : DEBUG(SSSDBG_CRIT_FAILURE,
271 : "setup_tls_config failed [%s]\n", strerror(ret));
272 0 : goto done;
273 : }
274 :
275 : /* setup SRV lookup plugin */
276 0 : hostname = dp_opt_get_string(ad_options->basic, AD_HOSTNAME);
277 0 : if (dp_opt_get_bool(ad_options->basic, AD_ENABLE_DNS_SITES)) {
278 : /* use AD plugin */
279 0 : ad_domain = dp_opt_get_string(ad_options->basic, AD_DOMAIN);
280 0 : ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE);
281 :
282 0 : srv_ctx = ad_srv_plugin_ctx_init(bectx, bectx->be_res,
283 0 : default_host_dbs, ad_options->id,
284 : hostname, ad_domain,
285 : ad_site_override);
286 0 : if (srv_ctx == NULL) {
287 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n");
288 0 : ret = ENOMEM;
289 0 : goto done;
290 : }
291 :
292 0 : be_fo_set_srv_lookup_plugin(bectx, ad_srv_plugin_send,
293 : ad_srv_plugin_recv, srv_ctx, "AD");
294 : } else {
295 : /* fall back to standard plugin */
296 0 : ret = be_fo_set_dns_srv_lookup_plugin(bectx, hostname);
297 0 : if (ret != EOK) {
298 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set SRV lookup plugin "
299 : "[%d]: %s\n", ret, strerror(ret));
300 0 : goto done;
301 : }
302 : }
303 :
304 : /* setup periodical refresh of expired records */
305 0 : ret = sdap_refresh_init(bectx->refresh_ctx, ad_ctx->sdap_id_ctx);
306 0 : if (ret != EOK && ret != EEXIST) {
307 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh "
308 : "will not work [%d]: %s\n", ret, strerror(ret));
309 : }
310 :
311 0 : *ops = &ad_id_ops;
312 0 : *pvt_data = ad_ctx;
313 :
314 0 : ret = EOK;
315 : done:
316 0 : if (ret != EOK) {
317 0 : talloc_zfree(ad_options->id_ctx);
318 : }
319 0 : return ret;
320 : }
321 :
322 : int
323 0 : sssm_ad_auth_init(struct be_ctx *bectx,
324 : struct bet_ops **ops,
325 : void **pvt_data)
326 : {
327 : errno_t ret;
328 0 : struct krb5_ctx *krb5_auth_ctx = NULL;
329 :
330 0 : if (!ad_options) {
331 0 : ret = common_ad_init(bectx);
332 0 : if (ret != EOK) {
333 0 : return ret;
334 : }
335 : }
336 :
337 0 : if (ad_options->auth_ctx) {
338 : /* Already initialized */
339 0 : *ops = &ad_auth_ops;
340 0 : *pvt_data = ad_options->auth_ctx;
341 0 : return EOK;
342 : }
343 :
344 0 : krb5_auth_ctx = talloc_zero(NULL, struct krb5_ctx);
345 0 : if (!krb5_auth_ctx) {
346 0 : ret = ENOMEM;
347 0 : goto done;
348 : }
349 :
350 0 : krb5_auth_ctx->config_type = K5C_GENERIC;
351 0 : krb5_auth_ctx->service = ad_options->service->krb5_service;
352 :
353 0 : ret = ad_get_auth_options(krb5_auth_ctx, ad_options, bectx,
354 : &krb5_auth_ctx->opts);
355 0 : if (ret != EOK) {
356 0 : DEBUG(SSSDBG_FATAL_FAILURE,
357 : "Could not determine Kerberos options\n");
358 0 : goto done;
359 : }
360 :
361 0 : ret = krb5_child_init(krb5_auth_ctx, bectx);
362 0 : if (ret != EOK) {
363 0 : DEBUG(SSSDBG_FATAL_FAILURE,
364 : "Could not initialize krb5_child settings: [%s]\n",
365 : strerror(ret));
366 0 : goto done;
367 : }
368 :
369 0 : ad_options->auth_ctx = talloc_steal(ad_options, krb5_auth_ctx);
370 0 : *ops = &ad_auth_ops;
371 0 : *pvt_data = ad_options->auth_ctx;
372 :
373 : done:
374 0 : if (ret != EOK) {
375 0 : talloc_free(krb5_auth_ctx);
376 : }
377 0 : return ret;
378 : }
379 :
380 : int
381 0 : sssm_ad_chpass_init(struct be_ctx *bectx,
382 : struct bet_ops **ops,
383 : void **pvt_data)
384 : {
385 : errno_t ret;
386 :
387 0 : if (!ad_options) {
388 0 : ret = common_ad_init(bectx);
389 0 : if (ret != EOK) {
390 0 : return ret;
391 : }
392 : }
393 :
394 0 : if (ad_options->auth_ctx) {
395 : /* Already initialized */
396 0 : *ops = &ad_chpass_ops;
397 0 : *pvt_data = ad_options->auth_ctx;
398 0 : return EOK;
399 : }
400 :
401 0 : ret = sssm_ad_auth_init(bectx, ops, pvt_data);
402 0 : *ops = &ad_chpass_ops;
403 0 : ad_options->auth_ctx = *pvt_data;
404 0 : return ret;
405 : }
406 :
407 : /* GPO parsing of PAM service names to Windows Logon Rights*/
408 : errno_t ad_gpo_parse_map_options(struct ad_access_ctx *access_ctx);
409 :
410 : int
411 0 : sssm_ad_access_init(struct be_ctx *bectx,
412 : struct bet_ops **ops,
413 : void **pvt_data)
414 : {
415 : errno_t ret;
416 : struct ad_access_ctx *access_ctx;
417 : struct ad_id_ctx *ad_id_ctx;
418 : const char *filter;
419 : const char *gpo_access_control_mode;
420 : int gpo_cache_timeout;
421 :
422 0 : access_ctx = talloc_zero(bectx, struct ad_access_ctx);
423 0 : if (!access_ctx) return ENOMEM;
424 :
425 0 : ret = sssm_ad_id_init(bectx, ops, (void **)&ad_id_ctx);
426 0 : if (ret != EOK) {
427 0 : goto fail;
428 : }
429 0 : access_ctx->ad_id_ctx = ad_id_ctx;
430 :
431 0 : ret = dp_copy_options(access_ctx, ad_options->basic, AD_OPTS_BASIC,
432 : &access_ctx->ad_options);
433 0 : if (ret != EOK) {
434 0 : DEBUG(SSSDBG_FATAL_FAILURE,
435 : "Could not initialize access provider options: [%s]\n",
436 : strerror(ret));
437 0 : goto fail;
438 : }
439 :
440 : /* Set up an sdap_access_ctx for checking expired/locked accounts */
441 0 : access_ctx->sdap_access_ctx =
442 0 : talloc_zero(access_ctx, struct sdap_access_ctx);
443 0 : if (!access_ctx->sdap_access_ctx) {
444 0 : ret = ENOMEM;
445 0 : goto fail;
446 : }
447 0 : access_ctx->sdap_access_ctx->id_ctx = ad_id_ctx->sdap_id_ctx;
448 :
449 : /* If ad_access_filter is set, the value of ldap_acess_order is
450 : * expire, filter, otherwise only expire
451 : */
452 0 : access_ctx->sdap_access_ctx->access_rule[0] = LDAP_ACCESS_EXPIRE;
453 0 : filter = dp_opt_get_cstring(access_ctx->ad_options, AD_ACCESS_FILTER);
454 0 : if (filter != NULL) {
455 : /* The processing of the extended filter is performed during the access
456 : * check itself
457 : */
458 0 : access_ctx->sdap_access_ctx->filter = talloc_strdup(
459 0 : access_ctx->sdap_access_ctx,
460 : filter);
461 0 : if (access_ctx->sdap_access_ctx->filter == NULL) {
462 0 : ret = ENOMEM;
463 0 : goto fail;
464 : }
465 :
466 0 : access_ctx->sdap_access_ctx->access_rule[1] = LDAP_ACCESS_FILTER;
467 0 : access_ctx->sdap_access_ctx->access_rule[2] = LDAP_ACCESS_EMPTY;
468 : } else {
469 0 : access_ctx->sdap_access_ctx->access_rule[1] = LDAP_ACCESS_EMPTY;
470 : }
471 :
472 : /* GPO access control mode */
473 0 : gpo_access_control_mode =
474 0 : dp_opt_get_string(access_ctx->ad_options, AD_GPO_ACCESS_CONTROL);
475 0 : if (strcasecmp(gpo_access_control_mode, "disabled") == 0) {
476 0 : access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_DISABLED;
477 0 : } else if (strcasecmp(gpo_access_control_mode, "permissive") == 0) {
478 0 : access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_PERMISSIVE;
479 0 : } else if (strcasecmp(gpo_access_control_mode, "enforcing") == 0) {
480 0 : access_ctx->gpo_access_control_mode = GPO_ACCESS_CONTROL_ENFORCING;
481 : } else {
482 0 : DEBUG(SSSDBG_FATAL_FAILURE,
483 : "Unrecognized GPO access control mode: %s\n",
484 : gpo_access_control_mode);
485 0 : ret = EINVAL;
486 0 : goto fail;
487 : }
488 :
489 : /* GPO cache timeout */
490 0 : gpo_cache_timeout =
491 0 : dp_opt_get_int(access_ctx->ad_options, AD_GPO_CACHE_TIMEOUT);
492 0 : access_ctx->gpo_cache_timeout = gpo_cache_timeout;
493 :
494 : /* GPO logon maps */
495 :
496 0 : ret = sss_hash_create(access_ctx, 10, &access_ctx->gpo_map_options_table);
497 0 : if (ret != EOK) {
498 0 : DEBUG(SSSDBG_FATAL_FAILURE,
499 : "Could not create gpo_map_options hash table: [%s]\n",
500 : strerror(ret));
501 0 : goto fail;
502 : }
503 :
504 0 : ret = ad_gpo_parse_map_options(access_ctx);
505 0 : if (ret != EOK) {
506 0 : DEBUG(SSSDBG_FATAL_FAILURE,
507 : "Could not parse gpo_map_options (invalid config): [%s]\n",
508 : strerror(ret));
509 0 : goto fail;
510 : }
511 :
512 0 : *ops = &ad_access_ops;
513 0 : *pvt_data = access_ctx;
514 :
515 0 : return EOK;
516 :
517 : fail:
518 0 : talloc_free(access_ctx);
519 0 : return ret;
520 : }
521 :
522 : static void
523 0 : ad_shutdown(struct be_req *req)
524 : {
525 : /* TODO: Clean up any internal data */
526 0 : sdap_handler_done(req, DP_ERR_OK, EOK, NULL);
527 0 : }
528 :
529 0 : int sssm_ad_subdomains_init(struct be_ctx *bectx,
530 : struct bet_ops **ops,
531 : void **pvt_data)
532 : {
533 : int ret;
534 : struct ad_id_ctx *id_ctx;
535 : const char *ad_domain;
536 :
537 0 : ret = sssm_ad_id_init(bectx, ops, (void **) &id_ctx);
538 0 : if (ret != EOK) {
539 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ad_id_init failed.\n");
540 0 : return ret;
541 : }
542 :
543 0 : if (ad_options == NULL) {
544 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Global AD options not available.\n");
545 0 : return EINVAL;
546 : }
547 :
548 0 : ad_domain = dp_opt_get_string(ad_options->basic, AD_DOMAIN);
549 :
550 0 : ret = ad_subdom_init(bectx, id_ctx, ad_domain, ops, pvt_data);
551 0 : if (ret != EOK) {
552 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ad_subdom_init failed.\n");
553 0 : return ret;
554 : }
555 :
556 0 : return EOK;
557 : }
558 :
559 :
560 0 : int sssm_ad_sudo_init(struct be_ctx *bectx,
561 : struct bet_ops **ops,
562 : void **pvt_data)
563 : {
564 : #ifdef BUILD_SUDO
565 : struct ad_id_ctx *id_ctx;
566 : int ret;
567 :
568 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing AD sudo handler\n");
569 :
570 0 : ret = sssm_ad_id_init(bectx, ops, (void **) &id_ctx);
571 0 : if (ret != EOK) {
572 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sssm_ad_id_init failed.\n");
573 0 : return ret;
574 : }
575 :
576 0 : return ad_sudo_init(bectx, id_ctx, ops, pvt_data);
577 : #else
578 : DEBUG(SSSDBG_MINOR_FAILURE, "Sudo init handler called but SSSD is "
579 : "built without sudo support, ignoring\n");
580 : return EOK;
581 : #endif
582 : }
|