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