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 /*
23 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <strings.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <thread.h>
33 #include <synch.h>
34 #include <sasl/sasl.h>
35 #include <sys/socket.h>
36 #include <netdb.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <syslog.h>
40 #include <ctype.h>
41 #include <libscf.h>
42 #include <libintl.h>
43 #include <locale.h>
44 #include "ns_sldap.h"
45 #include "ns_internal.h"
46
47 static int self_gssapi_only = 0;
48 static mutex_t self_gssapi_only_lock = DEFAULTMUTEX;
49
50 #define DNS_FMRI "svc:/network/dns/client:default"
51 #define MSGSIZE 256
52
53 #define NSSWITCH_CONF "/etc/nsswitch.conf"
54
55 /*
56 * Error Handling
57 */
58 #define CLIENT_FPRINTF if (mode_verbose && !mode_quiet) (void) fprintf
59
60 /*
61 * nscd calls this function to set self_gssapi_only flag so libsldap performs
62 * sasl/GSSAPI bind only. Also see comments of __ns_ldap_self_gssapi_config.
63 *
64 * Input: flag 0 use any kind of connection
65 * 1 use self/gssapi connection only
66 */
67 void
__ns_ldap_self_gssapi_only_set(int flag)68 __ns_ldap_self_gssapi_only_set(int flag) {
69 (void) mutex_lock(&self_gssapi_only_lock);
70 self_gssapi_only = flag;
71 (void) mutex_unlock(&self_gssapi_only_lock);
72 }
73
74 /*
75 * Get the flag value of self_gssapi_only
76 */
77 int
__s_api_self_gssapi_only_get(void)78 __s_api_self_gssapi_only_get(void) {
79 int flag;
80 (void) mutex_lock(&self_gssapi_only_lock);
81 flag = self_gssapi_only;
82 (void) mutex_unlock(&self_gssapi_only_lock);
83 return (flag);
84 }
85
86 /*
87 * nscd calls this function to detect the current native ldap configuration.
88 * The output are
89 * NS_LDAP_SELF_GSSAPI_CONFIG_NONE: No credential level self and
90 * no authentication method sasl/GSSAPI is
91 * configured.
92 * NS_LDAP_SELF_GSSAPI_CONFIG_ONLY: Only credential level self and
93 * authentication method sasl/GSSAPI are
94 * configured.
95 * NS_LDAP_SELF_GSSAPI_CONFIG_MIXED: More than one credential level are
96 * configured, including self.
97 * More than one authentication method
98 * are configured, including sasl/GSSAPI.
99 *
100 * __s_api_crosscheck makes sure self and sasl/GSSAPI pair up if they do
101 * get configured.
102 *
103 * When nscd detects it's MIXED case, it calls __ns_ldap_self_gssapi_only_set
104 * to force libsldap to do sasl/GSSAPI bind only for per-user lookup.
105 *
106 * Return: NS_LDAP_SUCCESS
107 * OTHERWISE - FAILURE
108 *
109 * Output: config. See comments above.
110 *
111 */
112 int
__ns_ldap_self_gssapi_config(ns_ldap_self_gssapi_config_t * config)113 __ns_ldap_self_gssapi_config(ns_ldap_self_gssapi_config_t *config) {
114 int self = 0, other_level = 0, gssapi = 0, other_method = 0;
115 ns_auth_t **aMethod = NULL, **aNext = NULL;
116 int **cLevel = NULL, **cNext = NULL, rc;
117 ns_ldap_error_t *errp = NULL;
118 FILE *fp;
119
120 if (config == NULL)
121 return (NS_LDAP_INVALID_PARAM);
122 else
123 *config = NS_LDAP_SELF_GSSAPI_CONFIG_NONE;
124
125 /*
126 * If config files don't exist, return NS_LDAP_CONFIG.
127 * It's the same return code __ns_ldap_getParam
128 * returns in the same situation.
129 */
130 if ((fp = fopen(NSCONFIGFILE, "rF")) == NULL)
131 return (NS_LDAP_CONFIG);
132 else
133 (void) fclose(fp);
134 if ((fp = fopen(NSCREDFILE, "rF")) == NULL)
135 return (NS_LDAP_CONFIG);
136 else
137 (void) fclose(fp);
138
139 /* Get the credential level list */
140 if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
141 (void ***)&cLevel, &errp)) != NS_LDAP_SUCCESS) {
142 if (errp)
143 (void) __ns_ldap_freeError(&errp);
144 if (cLevel)
145 (void) __ns_ldap_freeParam((void ***)&cLevel);
146 return (rc);
147 }
148 if (errp)
149 (void) __ns_ldap_freeError(&errp);
150 /* Get the authentication method list */
151 if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
152 (void ***)&aMethod, &errp)) != NS_LDAP_SUCCESS) {
153 if (errp)
154 (void) __ns_ldap_freeError(&errp);
155 if (cLevel)
156 (void) __ns_ldap_freeParam((void ***)&cLevel);
157 if (aMethod)
158 (void) __ns_ldap_freeParam((void ***)&aMethod);
159 return (rc);
160 }
161 if (errp)
162 (void) __ns_ldap_freeError(&errp);
163
164 if (cLevel == NULL || aMethod == NULL) {
165 if (cLevel)
166 (void) __ns_ldap_freeParam((void ***)&cLevel);
167 if (aMethod)
168 (void) __ns_ldap_freeParam((void ***)&aMethod);
169 return (NS_LDAP_SUCCESS);
170 }
171
172 for (cNext = cLevel; *cNext != NULL; cNext++) {
173 if (**cNext == NS_LDAP_CRED_SELF)
174 self++;
175 else
176 other_level++;
177 }
178 for (aNext = aMethod; *aNext != NULL; aNext++) {
179 if ((*aNext)->saslmech == NS_LDAP_SASL_GSSAPI)
180 gssapi++;
181 else
182 other_method++;
183 }
184
185 if (self > 0 && gssapi > 0) {
186 if (other_level == 0 && other_method == 0)
187 *config = NS_LDAP_SELF_GSSAPI_CONFIG_ONLY;
188 else
189 *config = NS_LDAP_SELF_GSSAPI_CONFIG_MIXED;
190 }
191
192 if (cLevel)
193 (void) __ns_ldap_freeParam((void ***)&cLevel);
194 if (aMethod)
195 (void) __ns_ldap_freeParam((void ***)&aMethod);
196 return (NS_LDAP_SUCCESS);
197 }
198
199 int
__s_api_sasl_bind_callback(LDAP * ld,unsigned flags,void * defaults,void * in)200 __s_api_sasl_bind_callback(
201 /* LINTED E_FUNC_ARG_UNUSED */
202 LDAP *ld,
203 /* LINTED E_FUNC_ARG_UNUSED */
204 unsigned flags,
205 void *defaults,
206 void *in)
207 {
208 char *ret = NULL;
209 sasl_interact_t *interact = in;
210 ns_sasl_cb_param_t *cred = (ns_sasl_cb_param_t *)defaults;
211
212
213 while (interact->id != SASL_CB_LIST_END) {
214
215 switch (interact->id) {
216
217 case SASL_CB_GETREALM:
218 ret = cred->realm;
219 break;
220 case SASL_CB_AUTHNAME:
221 ret = cred->authid;
222 break;
223 case SASL_CB_PASS:
224 ret = cred->passwd;
225 break;
226 case SASL_CB_USER:
227 ret = cred->authzid;
228 break;
229 case SASL_CB_NOECHOPROMPT:
230 case SASL_CB_ECHOPROMPT:
231 default:
232 break;
233 }
234
235 if (ret) {
236 /*
237 * No need to do strdup(ret), the data is always
238 * available in 'defaults' and libldap won't
239 * free it either. strdup(ret) causes memory
240 * leak.
241 */
242 interact->result = ret;
243 interact->len = strlen(ret);
244 } else {
245 interact->result = NULL;
246 interact->len = 0;
247 }
248 interact++;
249 }
250
251 return (LDAP_SUCCESS);
252 }
253
254 /*
255 * Find "dbase: service1 [...] services2" in fname and return
256 * " service1 [...] services2"
257 * e.g.
258 * Find "hosts: files dns" and return " files dns"
259 */
260 static char *
__ns_nsw_getconfig(const char * dbase,const char * fname,int * errp)261 __ns_nsw_getconfig(const char *dbase, const char *fname, int *errp)
262 {
263 FILE *fp = NULL;
264 char *linep, *retp = NULL;
265 char lineq[BUFSIZ], db_colon[BUFSIZ];
266
267 if ((fp = fopen(fname, "rF")) == NULL) {
268 *errp = NS_LDAP_CONFIG;
269 return (NULL);
270 }
271 *errp = NS_LDAP_SUCCESS;
272
273 while (linep = fgets(lineq, BUFSIZ, fp)) {
274 char *tokenp, *comment;
275
276 /*
277 * Ignore portion of line following the comment character '#'.
278 */
279 if ((comment = strchr(linep, '#')) != NULL) {
280 *comment = '\0';
281 }
282 if ((*linep == '\0') || isspace(*linep)) {
283 continue;
284 }
285 (void) snprintf(db_colon, BUFSIZ, "%s:", dbase);
286 if ((tokenp = strstr(linep, db_colon)) == NULL) {
287 continue; /* ignore this line */
288 } else {
289 /* skip "dbase:" */
290 retp = strdup(tokenp + strlen(db_colon));
291 if (retp == NULL)
292 *errp = NS_LDAP_MEMORY;
293 }
294 }
295
296 (void) fclose(fp);
297 return (retp);
298 }
299 /*
300 * Test the configurations of the "hosts" and "ipnodes"
301 * dns has to be present and appear before ldap
302 * e.g.
303 * "dns" , "dns files" "dns ldap files", "files dns" are allowed.
304 *
305 * Kerberos requires dns or it'd fail.
306 */
307 static int
test_dns_nsswitch(int foreground,const char * fname,ns_ldap_error_t ** errpp)308 test_dns_nsswitch(int foreground,
309 const char *fname,
310 ns_ldap_error_t **errpp) {
311 int ldap, dns, i, pserr, rc = NS_LDAP_SUCCESS;
312 char *db[3] = {"hosts", "ipnodes", NULL};
313 char buf[MSGSIZE], *conf = NULL, *token = NULL, *last = NULL;
314
315 for (i = 0; db[i] != NULL; i++) {
316 conf = __ns_nsw_getconfig(db[i], fname, &pserr);
317
318 if (conf == NULL) {
319 (void) snprintf(buf, MSGSIZE,
320 gettext("Parsing %s to find \"%s:\" "
321 "failed. err: %d"),
322 fname, db[i], pserr);
323 if (foreground) {
324 (void) fprintf(stderr, "%s\n", buf);
325 } else {
326 MKERROR(LOG_ERR, *errpp, NS_LDAP_CONFIG,
327 strdup(buf), NS_LDAP_MEMORY);
328 }
329 return (pserr);
330 }
331 ldap = dns = 0;
332 token = strtok_r(conf, " ", &last);
333 while (token != NULL) {
334 if (strncmp(token, "dns", 3) == 0) {
335 if (ldap) {
336 (void) snprintf(buf, MSGSIZE,
337 gettext("%s: ldap can't appear "
338 "before dns"), db[i]);
339 if (foreground) {
340 (void) fprintf(stderr,
341 "start: %s\n",
342 buf);
343 } else {
344 MKERROR(LOG_ERR, *errpp,
345 NS_LDAP_CONFIG,
346 strdup(buf),
347 NS_LDAP_MEMORY);
348 }
349 free(conf);
350 return (NS_LDAP_CONFIG);
351 } else {
352 dns++;
353 }
354 } else if (strncmp(token, "ldap", 4) == 0) {
355 ldap++;
356 }
357 /* next token */
358 token = strtok_r(NULL, " ", &last);
359 }
360 if (conf) {
361 free(conf);
362 conf = NULL;
363 }
364 if (!dns) {
365 (void) snprintf(buf, MSGSIZE,
366 gettext("%s: dns is not defined in "
367 "%s"), db[i], fname);
368 if (foreground) {
369 (void) fprintf(stderr, "start: %s\n", buf);
370 } else {
371 MKERROR(LOG_ERR, *errpp, NS_LDAP_CONFIG,
372 strdup(buf), NS_LDAP_MEMORY);
373 }
374 rc = NS_LDAP_CONFIG;
375 break;
376 }
377 }
378 return (rc);
379 }
380
381 static boolean_t
is_service(const char * fmri,const char * state)382 is_service(const char *fmri, const char *state) {
383 char *st;
384 boolean_t result = B_FALSE;
385
386 if ((st = smf_get_state(fmri)) != NULL) {
387 if (strcmp(st, state) == 0)
388 result = B_TRUE;
389 free(st);
390 }
391 return (result);
392 }
393
394
395 /*
396 * This function checks dns prerequisites for sasl/GSSAPI bind.
397 * It's called only if config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY ||
398 * config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED.
399 */
400 int
__ns_ldap_check_dns_preq(int foreground,int mode_verbose,int mode_quiet,const char * fname,ns_ldap_self_gssapi_config_t config,ns_ldap_error_t ** errpp)401 __ns_ldap_check_dns_preq(int foreground,
402 int mode_verbose,
403 int mode_quiet,
404 const char *fname,
405 ns_ldap_self_gssapi_config_t config,
406 ns_ldap_error_t **errpp) {
407
408 char buf[MSGSIZE];
409 int retcode = NS_LDAP_SUCCESS;
410 int loglevel;
411
412 if (errpp)
413 *errpp = NULL;
414 else
415 return (NS_LDAP_INVALID_PARAM);
416
417 if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
418 /* Shouldn't happen. Check this value just in case */
419 return (NS_LDAP_SUCCESS);
420
421 if ((retcode = test_dns_nsswitch(foreground, fname, errpp)) !=
422 NS_LDAP_SUCCESS)
423 return (retcode);
424
425 if (is_service(DNS_FMRI, SCF_STATE_STRING_ONLINE)) {
426 if (foreground) {
427 CLIENT_FPRINTF(stdout, "start: %s\n",
428 gettext("DNS client is enabled"));
429 } else {
430 syslog(LOG_INFO, "libsldap: %s",
431 gettext("DNS client is enabled"));
432 }
433 return (NS_LDAP_SUCCESS);
434 } else {
435 if (config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY) {
436 (void) snprintf(buf, MSGSIZE,
437 gettext("%s: DNS client is not enabled. "
438 "Run \"svcadm enable %s\". %s."),
439 "Error", DNS_FMRI, "Abort");
440 loglevel = LOG_ERR;
441 retcode = NS_LDAP_CONFIG;
442 } else if (config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED) {
443 (void) snprintf(buf, MSGSIZE,
444 gettext("%s: DNS client is not enabled. "
445 "Run \"svcadm enable %s\". %s."
446 "Fall back to other cred level/bind. "),
447 "Warning", DNS_FMRI, "Continue");
448 loglevel = LOG_INFO;
449 retcode = NS_LDAP_SUCCESS;
450 }
451
452 if (foreground) {
453 (void) fprintf(stderr, "start: %s\n", buf);
454 } else {
455 MKERROR(loglevel, *errpp, retcode, strdup(buf),
456 NS_LDAP_MEMORY);
457 }
458 return (retcode);
459 }
460 }
461
462 /*
463 * Check if sasl/GSSAPI works
464 */
465 int
__ns_ldap_check_gssapi_preq(int foreground,int mode_verbose,int mode_quiet,ns_ldap_self_gssapi_config_t config,ns_ldap_error_t ** errpp)466 __ns_ldap_check_gssapi_preq(int foreground,
467 int mode_verbose,
468 int mode_quiet,
469 ns_ldap_self_gssapi_config_t config,
470 ns_ldap_error_t **errpp) {
471
472 int rc;
473 char *attr[2] = {"dn", NULL}, buf[MSGSIZE];
474 ns_cred_t cred;
475 ns_ldap_result_t *result = NULL;
476 int loglevel;
477
478 if (errpp)
479 *errpp = NULL;
480 else
481 return (NS_LDAP_INVALID_PARAM);
482
483 if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
484 /* Don't need to check */
485 return (NS_LDAP_SUCCESS);
486
487 (void) memset(&cred, 0, sizeof (ns_cred_t));
488
489 cred.auth.type = NS_LDAP_AUTH_SASL;
490 cred.auth.tlstype = NS_LDAP_TLS_NONE;
491 cred.auth.saslmech = NS_LDAP_SASL_GSSAPI;
492
493 rc = __ns_ldap_list(NULL, (const char *)"objectclass=*",
494 NULL, (const char **)attr, &cred,
495 NS_LDAP_SCOPE_BASE, &result, errpp, NULL, NULL);
496 if (result)
497 (void) __ns_ldap_freeResult(&result);
498
499 if (rc == NS_LDAP_SUCCESS) {
500 if (foreground) {
501 CLIENT_FPRINTF(stdout, "start: %s\n",
502 gettext("sasl/GSSAPI bind works"));
503 } else {
504 syslog(LOG_INFO, "libsldap: %s",
505 gettext("sasl/GSSAPI bind works"));
506 }
507 return (NS_LDAP_SUCCESS);
508 } else {
509 if (config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY) {
510 (void) snprintf(buf, MSGSIZE,
511 gettext("%s: sasl/GSSAPI bind is not "
512 "working. %s."),
513 "Error", "Abort");
514 loglevel = LOG_ERR;
515 } else if (config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED) {
516 (void) snprintf(buf, MSGSIZE,
517 gettext("%s: sasl/GSSAPI bind is not "
518 "working. Fall back to other cred "
519 "level/bind. %s."),
520 "Warning", "Continue");
521 loglevel = LOG_INFO;
522 /* reset return code */
523 rc = NS_LDAP_SUCCESS;
524 }
525
526 if (foreground) {
527 (void) fprintf(stderr, "start: %s\n", buf);
528 } else {
529 MKERROR(loglevel, *errpp, rc, strdup(buf),
530 NS_LDAP_MEMORY);
531 }
532 return (rc);
533 }
534 }
535 /*
536 * This is called by ldap_cachemgr to check dns and gssapi prequisites.
537 */
538 int
__ns_ldap_check_all_preq(int foreground,int mode_verbose,int mode_quiet,ns_ldap_self_gssapi_config_t config,ns_ldap_error_t ** errpp)539 __ns_ldap_check_all_preq(int foreground,
540 int mode_verbose,
541 int mode_quiet,
542 ns_ldap_self_gssapi_config_t config,
543 ns_ldap_error_t **errpp) {
544
545 int rc;
546
547 if (errpp)
548 *errpp = NULL;
549 else
550 return (NS_LDAP_INVALID_PARAM);
551
552 if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
553 /* Don't need to check */
554 return (NS_LDAP_SUCCESS);
555
556 if ((rc = __ns_ldap_check_dns_preq(foreground,
557 mode_verbose, mode_quiet, NSSWITCH_CONF,
558 config, errpp)) != NS_LDAP_SUCCESS)
559 return (rc);
560 if ((rc = __ns_ldap_check_gssapi_preq(foreground,
561 mode_verbose, mode_quiet, config, errpp)) !=
562 NS_LDAP_SUCCESS)
563 return (rc);
564
565 return (NS_LDAP_SUCCESS);
566 }
567