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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <errno.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include "ns_sldap.h" 36 37 #include <nss_dbdefs.h> 38 #include <nsswitch.h> 39 40 #include <pwd.h> 41 #include <shadow.h> 42 #include <syslog.h> 43 44 #include "passwdutil.h" 45 46 #include "utils.h" 47 48 int ldap_getattr(char *name, attrlist *item, pwu_repository_t *rep); 49 int ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, 50 void **buf); 51 int ldap_update(attrlist *items, pwu_repository_t *rep, void *buf); 52 int ldap_putpwnam(char *name, char *oldpw, char *dummy, 53 pwu_repository_t *rep, void *buf); 54 int ldap_user_to_authenticate(char *name, pwu_repository_t *rep, 55 char **auth_user, int *privileged); 56 57 /* 58 * ldap function pointer table, used by passwdutil_init to initialize 59 * the global Repository-OPerations table "rops" 60 */ 61 struct repops ldap_repops = { 62 NULL, /* checkhistory */ 63 ldap_getattr, 64 ldap_getpwnam, 65 ldap_update, 66 ldap_putpwnam, 67 ldap_user_to_authenticate, 68 NULL, /* lock */ 69 NULL /* unlock */ 70 }; 71 72 /* 73 * structure used to keep state between get/update/put calls 74 */ 75 typedef struct { 76 char *passwd; /* encrypted password */ 77 struct passwd *pwd; 78 ns_ldap_attr_t **attrs; 79 } ldapbuf_t; 80 81 /* 82 * The following define's are taken from 83 * usr/src/lib/nsswitch/ldap/common/getpwnam.c 84 */ 85 86 /* passwd attributes filters */ 87 #define _PWD_CN "cn" 88 #define _PWD_UID "uid" 89 #define _PWD_USERPASSWORD "userpassword" 90 #define _PWD_UIDNUMBER "uidnumber" 91 #define _PWD_GIDNUMBER "gidnumber" 92 #define _PWD_GECOS "gecos" 93 #define _PWD_DESCRIPTION "description" 94 #define _PWD_HOMEDIRECTORY "homedirectory" 95 #define _PWD_LOGINSHELL "loginshell" 96 97 /* 98 * int ldap_user_to_authenticate(user, rep, auth_user, privileged) 99 * 100 * We can't determine whether the user is "privileged" in the LDAP 101 * sense. The operation should be attempted and will succeed if 102 * the user had privileges. 103 * 104 * For our purposes, we say that the user is privileged if he/she 105 * is attempting to change another user's password attributes. 106 */ 107 int 108 ldap_user_to_authenticate(char *user, pwu_repository_t *rep, 109 char **auth_user, int *privileged) 110 { 111 struct passwd *pw; 112 uid_t uid; 113 uid_t priviledged_uid; 114 int res = PWU_SUCCESS; 115 116 if (strcmp(user, "root") == 0) 117 return (PWU_NOT_FOUND); 118 119 if ((pw = getpwnam_from(user, rep, REP_LDAP)) == NULL) 120 return (PWU_NOT_FOUND); 121 122 uid = getuid(); 123 124 if (uid == pw->pw_uid) { 125 /* changing out own, not privileged */ 126 *privileged = 0; 127 if ((*auth_user = strdup(user)) == NULL) 128 res = PWU_NOMEM; 129 } else { 130 char pwd_buf[1024]; 131 struct passwd pwr; 132 133 *privileged = 1; 134 /* 135 * specific case for root 136 * we want 'user' to be authenticated. 137 */ 138 if (uid == 0) { 139 priviledged_uid = pw->pw_uid; 140 } else { 141 priviledged_uid = uid; 142 } 143 if (getpwuid_r(priviledged_uid, &pwr, pwd_buf, 144 sizeof (pwd_buf)) != NULL) { 145 if ((*auth_user = strdup(pwr.pw_name)) == NULL) 146 res = PWU_NOMEM; 147 } else { 148 /* hmm. can't find name of current user...??? */ 149 150 #define MAX_UID_LEN 11 /* UID's larger than 2^32 won't fit... */ 151 if ((*auth_user = malloc(MAX_UID_LEN)) == NULL) { 152 res = PWU_NOMEM; 153 } else { 154 (void) snprintf(*auth_user, MAX_UID_LEN, "%d", 155 (int)uid); 156 } 157 } 158 } 159 160 return (res); 161 } 162 163 /* 164 * int ldap_getattr(name, item, rep) 165 * 166 * retrieve attributes specified in "item" for user "name". 167 */ 168 /*ARGSUSED*/ 169 int 170 ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep) 171 { 172 int res; 173 struct passwd *pw = NULL; 174 struct spwd *spw = NULL; 175 attrlist *w; 176 177 int need_shadow = 0; /* Need shadow info from LDAP server */ 178 int need_normal = 0; /* Need non-shadow info from LDAP server */ 179 180 /* We need the "shadow" map for the password only */ 181 for (w = items; w != NULL; w = w->next) { 182 if (w->type == ATTR_PASSWD || 183 w->type == ATTR_PASSWD_SERVER_POLICY) 184 need_shadow = 1; 185 else 186 need_normal = 1; 187 } 188 189 if (need_normal) { 190 res = dup_pw(&pw, getpwnam_from(name, rep, REP_LDAP)); 191 if (res != PWU_SUCCESS) 192 goto out; 193 } 194 195 if (need_shadow) { 196 res = dup_spw(&spw, getspnam_from(name, rep, REP_LDAP)); 197 if (res != PWU_SUCCESS) { 198 goto out; 199 } 200 } 201 202 for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) { 203 switch (w->type) { 204 case ATTR_NAME: 205 if ((w->data.val_s = strdup(pw->pw_name)) == NULL) 206 res = PWU_NOMEM; 207 break; 208 case ATTR_COMMENT: 209 if ((w->data.val_s = strdup(pw->pw_comment)) == NULL) 210 res = PWU_NOMEM; 211 break; 212 case ATTR_GECOS: 213 if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL) 214 res = PWU_NOMEM; 215 break; 216 case ATTR_HOMEDIR: 217 if ((w->data.val_s = strdup(pw->pw_dir)) == NULL) 218 res = PWU_NOMEM; 219 break; 220 case ATTR_SHELL: 221 if ((w->data.val_s = strdup(pw->pw_shell)) == NULL) 222 res = PWU_NOMEM; 223 break; 224 case ATTR_PASSWD: 225 case ATTR_PASSWD_SERVER_POLICY: 226 if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL) 227 res = PWU_NOMEM; 228 break; 229 case ATTR_AGE: 230 if ((w->data.val_s = strdup(pw->pw_age)) == NULL) 231 res = PWU_NOMEM; 232 break; 233 case ATTR_REP_NAME: 234 if ((w->data.val_s = strdup("ldap")) == NULL) 235 res = PWU_NOMEM; 236 break; 237 238 /* integer values */ 239 case ATTR_UID: 240 w->data.val_i = pw->pw_uid; 241 break; 242 case ATTR_GID: 243 w->data.val_i = pw->pw_gid; 244 break; 245 case ATTR_LSTCHG: 246 w->data.val_i = -1; 247 break; 248 case ATTR_MIN: 249 w->data.val_i = -1; 250 break; 251 case ATTR_MAX: 252 w->data.val_i = -1; 253 break; 254 case ATTR_WARN: 255 w->data.val_i = -1; 256 break; 257 case ATTR_INACT: 258 w->data.val_i = -1; 259 break; 260 case ATTR_EXPIRE: 261 w->data.val_i = -1; 262 break; 263 case ATTR_FLAG: 264 break; 265 default: 266 break; 267 } 268 } 269 270 out: 271 if (pw) 272 free_pwd(pw); 273 if (spw) 274 free_spwd(spw); 275 276 return (res); 277 } 278 279 /* 280 * int ldap_getpwnam(name, items, rep, buf) 281 * 282 * There is no need to get the old values from the ldap 283 * server, as the update will update each item individually. 284 * Therefore, we only allocate a buffer that will be used by 285 * _update and _putpwnam to hold the attributes to update. 286 * 287 * Only when we're about to update a password, we need to retrieve 288 * the old password since it contains salt-information. 289 */ 290 /*ARGSUSED*/ 291 int 292 ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, 293 void **buf) 294 { 295 attrlist *p; 296 int nr_items; 297 int need_pwd = 0; 298 ldapbuf_t *ldapbuf; 299 int res; 300 301 for (nr_items = 0, p = items; p != NULL; p = p->next) { 302 nr_items++; 303 if (p->type == ATTR_PASSWD || 304 p->type == ATTR_PASSWD_SERVER_POLICY) 305 need_pwd = 1; 306 } 307 308 309 ldapbuf = calloc(1, sizeof (ldapbuf_t)); 310 if (ldapbuf == NULL) 311 return (PWU_NOMEM); 312 313 ldapbuf->attrs = calloc(nr_items, sizeof (ns_ldap_attr_t *)); 314 if (ldapbuf->attrs == NULL) 315 return (PWU_NOMEM); 316 317 if (need_pwd) { 318 struct spwd *spw; 319 320 res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP)); 321 if (res != PWU_SUCCESS) 322 return (res); 323 324 spw = getspnam_from(name, rep, REP_LDAP); 325 if (spw) { 326 ldapbuf->passwd = strdup(spw->sp_pwdp); 327 if (ldapbuf->passwd == NULL) 328 return (PWU_NOMEM); 329 } 330 } 331 332 *buf = ldapbuf; 333 return (0); 334 } 335 336 /* 337 * new_attr(name, value) 338 * 339 * create a new LDAP attribute to be sent to the server 340 */ 341 ns_ldap_attr_t * 342 new_attr(char *name, char *value) 343 { 344 ns_ldap_attr_t *tmp; 345 346 tmp = malloc(sizeof (*tmp)); 347 if (tmp != NULL) { 348 tmp->attrname = name; 349 tmp->attrvalue = (char **)calloc(2, sizeof (char *)); 350 if (tmp->attrvalue == NULL) { 351 free(tmp); 352 return (NULL); 353 } 354 tmp->attrvalue[0] = value; 355 tmp->value_count = 1; 356 } 357 358 return (tmp); 359 } 360 361 /* 362 * ldap_update(items, rep, buf) 363 * 364 * create LDAP attributes in 'buf' for each attribute in 'items'. 365 */ 366 /*ARGSUSED*/ 367 int 368 ldap_update(attrlist *items, pwu_repository_t *rep, void *buf) 369 { 370 attrlist *p; 371 int idx = 0; 372 ldapbuf_t *ldapbuf = (ldapbuf_t *)buf; 373 ns_ldap_attr_t **attrs = ldapbuf->attrs; 374 char *pwd, *val; 375 char *salt; 376 size_t cryptlen; 377 378 for (p = items; p != NULL; p = p->next) { 379 switch (p->type) { 380 case ATTR_PASSWD: 381 salt = crypt_gensalt(ldapbuf->passwd, ldapbuf->pwd); 382 383 if (salt == NULL) { 384 if (errno == ENOMEM) 385 return (PWU_NOMEM); 386 else { 387 /* algorithm problem? */ 388 syslog(LOG_AUTH | LOG_ALERT, 389 "passwdutil: crypt_gensalt " 390 "%m"); 391 return (PWU_UPDATE_FAILED); 392 } 393 } 394 395 pwd = crypt(p->data.val_s, salt); 396 free(salt); 397 cryptlen = strlen(pwd) + sizeof ("{crypt}"); 398 val = malloc(cryptlen); 399 if (val == NULL) 400 return (PWU_NOMEM); 401 (void) snprintf(val, cryptlen, "{crypt}%s", pwd); 402 403 attrs[idx] = new_attr(_PWD_USERPASSWORD, val); 404 break; 405 /* 406 * For server policy, don't crypt the password, 407 * send the password as is to the server and 408 * let the LDAP server do its own password 409 * encryption 410 */ 411 case ATTR_PASSWD_SERVER_POLICY: 412 val = strdup(p->data.val_s); 413 if (val == NULL) 414 return (PWU_NOMEM); 415 416 attrs[idx] = new_attr(_PWD_USERPASSWORD, val); 417 break; 418 case ATTR_COMMENT: 419 /* XX correct? */ 420 attrs[idx] = new_attr(_PWD_DESCRIPTION, p->data.val_s); 421 break; 422 case ATTR_GECOS: 423 attrs[idx] = new_attr(_PWD_GECOS, p->data.val_s); 424 break; 425 case ATTR_HOMEDIR: 426 attrs[idx] = new_attr(_PWD_HOMEDIRECTORY, 427 p->data.val_s); 428 break; 429 case ATTR_SHELL: 430 attrs[idx] = new_attr(_PWD_LOGINSHELL, p->data.val_s); 431 break; 432 /* Unsupported items are below this line */ 433 case ATTR_NAME: 434 case ATTR_UID: 435 case ATTR_GID: 436 case ATTR_AGE: 437 case ATTR_LSTCHG: 438 case ATTR_MIN: 439 case ATTR_MAX: 440 case ATTR_WARN: 441 case ATTR_INACT: 442 case ATTR_EXPIRE: 443 case ATTR_FLAG: 444 break; 445 default: 446 break; 447 } 448 if (attrs[idx] == NULL) 449 return (PWU_NOMEM); 450 idx++; 451 } 452 453 attrs[idx] = NULL; 454 455 return (PWU_SUCCESS); 456 } 457 458 /* 459 * ldap_to_pwu_code(error, pwd_status) 460 * 461 * translation from LDAP return values and PWU return values 462 */ 463 int 464 ldap_to_pwu_code(int error, int pwd_status) 465 { 466 switch (error) { 467 case NS_LDAP_SUCCESS: return (PWU_SUCCESS); 468 case NS_LDAP_OP_FAILED: return (PWU_DENIED); 469 case NS_LDAP_NOTFOUND: return (PWU_NOT_FOUND); 470 case NS_LDAP_MEMORY: return (PWU_NOMEM); 471 case NS_LDAP_CONFIG: return (PWU_NOT_FOUND); 472 case NS_LDAP_INTERNAL: 473 switch (pwd_status) { 474 case NS_PASSWD_EXPIRED: 475 return (PWU_DENIED); 476 case NS_PASSWD_CHANGE_NOT_ALLOWED: 477 return (PWU_CHANGE_NOT_ALLOWED); 478 case NS_PASSWD_TOO_SHORT: 479 return (PWU_PWD_TOO_SHORT); 480 case NS_PASSWD_INVALID_SYNTAX: 481 return (PWU_PWD_INVALID); 482 case NS_PASSWD_IN_HISTORY: 483 return (PWU_PWD_IN_HISTORY); 484 case NS_PASSWD_WITHIN_MIN_AGE: 485 return (PWU_WITHIN_MIN_AGE); 486 default: 487 return (PWU_SYSTEM_ERROR); 488 } 489 default: return (PWU_SYSTEM_ERROR); 490 } 491 } 492 493 int 494 ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn, 495 const char *pwd, int *pwd_status) 496 { 497 int result = NS_LDAP_OP_FAILED; 498 int ldaprc; 499 int authstried = 0; 500 char **certpath = NULL; 501 ns_auth_t **app; 502 ns_auth_t **authpp = NULL; 503 ns_auth_t *authp = NULL; 504 ns_cred_t *credp; 505 ns_ldap_error_t *errorp = NULL; 506 507 debug("%s: replace_ldapattr()", __FILE__); 508 509 if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL) 510 return (PWU_NOMEM); 511 512 /* Fill in the user name and password */ 513 if (dn == NULL || pwd == NULL) 514 goto out; 515 516 credp->cred.unix_cred.userID = strdup(binddn); 517 credp->cred.unix_cred.passwd = strdup(pwd); 518 519 /* get host certificate path, if one is configured */ 520 ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P, 521 (void ***)&certpath, &errorp); 522 if (ldaprc != NS_LDAP_SUCCESS) 523 goto out; 524 525 if (certpath && *certpath) 526 credp->hostcertpath = *certpath; 527 528 /* Load the service specific authentication method */ 529 ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp, 530 &errorp); 531 532 if (ldaprc != NS_LDAP_SUCCESS) 533 goto out; 534 535 /* 536 * if authpp is null, there is no serviceAuthenticationMethod 537 * try default authenticationMethod 538 */ 539 if (authpp == NULL) { 540 ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp, 541 &errorp); 542 if (ldaprc != NS_LDAP_SUCCESS) 543 goto out; 544 } 545 546 /* 547 * if authpp is still null, then can not authenticate, syslog 548 * error message and return error 549 */ 550 if (authpp == NULL) { 551 syslog(LOG_ERR, 552 "passwdutil: no legal LDAP authentication method configured"); 553 result = NS_LDAP_OP_FAILED; 554 goto out; 555 } 556 557 /* 558 * Walk the array and try all authentication methods in order except 559 * for "none". 560 */ 561 for (app = authpp; *app; app++) { 562 authp = *app; 563 /* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */ 564 if (authp->type == NS_LDAP_AUTH_NONE) 565 continue; 566 authstried++; 567 credp->auth.type = authp->type; 568 credp->auth.tlstype = authp->tlstype; 569 credp->auth.saslmech = authp->saslmech; 570 credp->auth.saslopt = authp->saslopt; 571 572 ldaprc = __ns_ldap_repAttr("shadow", dn, 573 (const ns_ldap_attr_t * const *)attrs, 574 credp, 0, &errorp); 575 if (ldaprc == NS_LDAP_SUCCESS) { 576 result = NS_LDAP_SUCCESS; 577 goto out; 578 } 579 580 /* 581 * other errors might need to be added to this list, for 582 * the current supported mechanisms this is sufficient 583 */ 584 if ((ldaprc == NS_LDAP_INTERNAL) && 585 (errorp->pwd_mgmt.status == NS_PASSWD_GOOD) && 586 ((errorp->status == LDAP_INAPPROPRIATE_AUTH) || 587 (errorp->status == LDAP_INVALID_CREDENTIALS))) { 588 result = ldaprc; 589 goto out; 590 } 591 592 /* 593 * If there is error related to password policy, 594 * return it to caller 595 */ 596 if ((ldaprc == NS_LDAP_INTERNAL) && 597 errorp->pwd_mgmt.status != NS_PASSWD_GOOD) { 598 *pwd_status = errorp->pwd_mgmt.status; 599 result = ldaprc; 600 goto out; 601 } else 602 *pwd_status = NS_PASSWD_GOOD; 603 604 /* we don't really care about the error, just clean it up */ 605 if (errorp) 606 (void) __ns_ldap_freeError(&errorp); 607 } 608 if (authstried == 0) { 609 syslog(LOG_ERR, 610 "passwdutil: no legal LDAP authentication method configured"); 611 result = NS_LDAP_CONFIG; 612 goto out; 613 } 614 result = PWU_DENIED; 615 616 out: 617 if (credp) 618 (void) __ns_ldap_freeCred(&credp); 619 620 if (authpp) 621 (void) __ns_ldap_freeParam((void ***)&authpp); 622 623 if (errorp) 624 (void) __ns_ldap_freeError(&errorp); 625 626 return (result); 627 } 628 629 630 631 /* 632 * ldap_putpwnam(name, oldpw, dummy, rep, buf) 633 * 634 * update the LDAP server with the attributes contained in 'buf'. 635 * The dummy parameter is a placeholder for NIS+ where the old 636 * RPC password is passwd. 637 */ 638 /*ARGSUSED*/ 639 int 640 ldap_putpwnam(char *name, char *oldpw, char *dummy, 641 pwu_repository_t *rep, void *buf) 642 { 643 int res; 644 char *dn; /* dn of user whose attributes we are changing */ 645 char *binddn; /* dn of user who is performing the change */ 646 ns_ldap_error_t *errorp; 647 ldapbuf_t *ldapbuf = (ldapbuf_t *)buf; 648 ns_ldap_attr_t **attrs = ldapbuf->attrs; 649 struct passwd *pw; 650 int pwd_status; 651 uid_t uid; 652 653 if (strcmp(name, "root") == 0) 654 return (PWU_NOT_FOUND); 655 656 /* 657 * The LDAP server checks whether we are permitted to perform 658 * the requested change. We need to send the name of the user 659 * who is executing this piece of code, together with his 660 * current password to the server. 661 * If this is executed by a normal user changing his/her own 662 * password, this will simply be the OLD password that is to 663 * be changed. 664 * Specific case if the user who is executing this piece 665 * of code is root. We will then issue the LDAP request 666 * with the DN of the user we want to change the passwd of. 667 */ 668 669 /* 670 * convert name of user whose attributes we are changing 671 * to a distinguished name 672 */ 673 res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp); 674 if (res != NS_LDAP_SUCCESS) 675 goto out; 676 677 /* 678 * create a dn for the user who is executing this code 679 */ 680 uid = getuid(); 681 if (uid == 0) { 682 if ((pw = getpwnam_from(name, rep, REP_LDAP)) == NULL) { 683 res = NS_LDAP_OP_FAILED; 684 goto out; 685 } 686 } else if ((pw = getpwuid_from(uid, rep, REP_LDAP)) == NULL) { 687 /* 688 * User executing this code is not known to the LDAP 689 * server. This operation is to be denied 690 */ 691 res = NS_LDAP_OP_FAILED; 692 goto out; 693 } 694 695 res = __ns_ldap_uid2dn(pw->pw_name, &binddn, NULL, &errorp); 696 if (res != NS_LDAP_SUCCESS) 697 goto out; 698 699 res = ldap_replaceattr(dn, attrs, binddn, oldpw, 700 &pwd_status); 701 702 out: 703 while (*attrs) { 704 free((*attrs)->attrvalue[0]); 705 free(*attrs); 706 attrs++; 707 } 708 if (ldapbuf->passwd) { 709 (void) memset(ldapbuf->passwd, 0, strlen(ldapbuf->passwd)); 710 free(ldapbuf->passwd); 711 } 712 if (ldapbuf->pwd) 713 free_pwd(ldapbuf->pwd); 714 free(dn); 715 716 return (ldap_to_pwu_code(res, pwd_status)); 717 } 718