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 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 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 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 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 * 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 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 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 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 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 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