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