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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <nsswitch.h> 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <string.h> 31 #include <syslog.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 35 #include "ns_sldap.h" 36 #include <nss_dbdefs.h> 37 #include <nsswitch.h> 38 #include <pwd.h> 39 #include <shadow.h> 40 #include <rpcsvc/nis.h> 41 42 #include "passwdutil.h" 43 44 /* 45 * name_to_int(rep) 46 * 47 * Translate the repository to a bitmask. 48 * if we don't recognise the repository name, we return REP_ERANGE 49 */ 50 int 51 name_to_int(char *rep_name) 52 { 53 int result = REP_ERANGE; 54 55 if (strcmp(rep_name, "files") == 0) 56 result = REP_FILES; 57 else if (strcmp(rep_name, "nis") == 0) 58 result = REP_NIS; 59 else if (strcmp(rep_name, "ldap") == 0) 60 result = REP_LDAP; 61 else if (strcmp(rep_name, "compat") == 0) { 62 struct __nsw_switchconfig *cfg; 63 enum __nsw_parse_err pserr; 64 65 cfg = __nsw_getconfig("passwd_compat", &pserr); 66 if (cfg == NULL) { 67 result = REP_FILES | REP_NIS; 68 } else { 69 if (strcmp(cfg->lookups->service_name, "ldap") == 0) 70 result = REP_FILES | REP_LDAP; 71 else 72 result = REP_ERANGE; 73 __nsw_freeconfig(cfg); 74 } 75 } 76 77 return (result); 78 } 79 80 /* 81 * Figure out which repository we use in compat mode. 82 */ 83 int 84 get_compat_mode(void) 85 { 86 struct __nsw_switchconfig *cfg; 87 enum __nsw_parse_err pserr; 88 int result = REP_COMPAT_NIS; 89 90 if ((cfg = __nsw_getconfig("passwd_compat", &pserr)) != NULL) { 91 if (strcmp(cfg->lookups->service_name, "ldap") == 0) 92 result = REP_COMPAT_LDAP; 93 } 94 __nsw_freeconfig(cfg); 95 96 return (result); 97 } 98 99 /* 100 * get_ns(rep, accesstype) 101 * 102 * returns a bitmask of repositories to use based on either 103 * 1. the repository that is given as argument 104 * 2. the nsswitch.conf file 105 * 3. the type of access requested 106 * 107 * "accesstype" indicates whether we are reading from or writing to the 108 * repository. We need to know this since "compat" will translate into 109 * REP_NSS (the nss-switch) for READ access (needed to decode 110 * the black-magic '+' entries) but it translates into a bitmask 111 * on WRITE access. 112 * 113 * If we detect read-access in compat mode, we augment the result 114 * with one of REP_COMPAT_{NIS,LDAP}. We need this in order to 115 * implement ATTR_REP_NAME in nss_getpwnam. 116 * 117 * A return value of REP_NOREP indicates an error. 118 */ 119 int 120 get_ns(pwu_repository_t *rep, int accesstype) 121 { 122 struct __nsw_switchconfig *conf = NULL; 123 enum __nsw_parse_err pserr; 124 struct __nsw_lookup *lkp; 125 struct __nsw_lookup *lkp2; 126 struct __nsw_lookup *lkp3; 127 struct __nsw_lookup *lkpn; 128 int result = REP_NOREP; 129 130 if (rep != PWU_DEFAULT_REP) { 131 result = name_to_int(rep->type); 132 return (result); 133 } 134 135 conf = __nsw_getconfig("passwd", &pserr); 136 if (conf == NULL) { 137 /* 138 * No config found. The user didn't supply a repository, 139 * so we try to change the password in the default 140 * repositories (files and nis) even though we cannot 141 * find the name service switch entry. (Backward compat) 142 */ 143 syslog(LOG_ERR, "passwdutil.so: nameservice switch entry for " 144 "passwd not found."); 145 result = REP_FILES | REP_NIS; 146 return (result); 147 } 148 149 lkp = conf->lookups; 150 151 /* 152 * Supported nsswitch.conf can have a maximum of 3 repositories. 153 * If we encounter an unsupported nsswitch.conf, we return REP_NSS 154 * to fall back to the nsswitch backend. 155 * 156 * Note that specifying 'ad' in the configuration is acceptable 157 * though changing AD users' passwords through passwd(1) is not. 158 * Therefore "ad" will be silently ignored. 159 */ 160 if (conf->num_lookups == 1) { 161 /* files or compat */ 162 163 if (strcmp(lkp->service_name, "files") == 0) { 164 result = name_to_int(lkp->service_name); 165 } else if (strcmp(lkp->service_name, "compat") == 0) { 166 if (accesstype == PWU_READ) 167 result = REP_NSS | get_compat_mode(); 168 else 169 result = name_to_int(lkp->service_name); 170 } else 171 result = REP_NSS; 172 173 } else if (conf->num_lookups == 2) { 174 lkp2 = lkp->next; 175 if (strcmp(lkp->service_name, "files") == 0) { 176 result = REP_FILES; 177 if (strcmp(lkp2->service_name, "ldap") == 0) 178 result |= REP_LDAP; 179 else if (strcmp(lkp2->service_name, "nis") == 0) 180 result |= REP_NIS; 181 else if (strcmp(lkp2->service_name, "ad") != 0) 182 result = REP_NSS; 183 /* AD is ignored */ 184 } else { 185 result = REP_NSS; 186 } 187 } else if (conf->num_lookups == 3) { 188 /* 189 * Valid configurations with 3 repositories are: 190 * files ad [nis | ldap ] OR 191 * files [nis | ldap ] ad 192 */ 193 lkp2 = lkp->next; 194 lkp3 = lkp2->next; 195 if (strcmp(lkp2->service_name, "ad") == 0) 196 lkpn = lkp3; 197 else if (strcmp(lkp3->service_name, "ad") == 0) 198 lkpn = lkp2; 199 else 200 lkpn = NULL; 201 if (strcmp(lkp->service_name, "files") == 0 && 202 lkpn != NULL) { 203 result = REP_FILES; 204 if (strcmp(lkpn->service_name, "ldap") == 0) 205 result |= REP_LDAP; 206 else if (strcmp(lkpn->service_name, "nis") == 0) 207 result |= REP_NIS; 208 else 209 result = REP_NSS; 210 } else { 211 result = REP_NSS; 212 } 213 } else { 214 result = REP_NSS; 215 } 216 217 __nsw_freeconfig(conf); 218 return (result); 219 } 220 221 static void 222 nss_ldap_passwd(p) 223 nss_db_params_t *p; 224 { 225 p->name = NSS_DBNAM_PASSWD; 226 p->flags |= NSS_USE_DEFAULT_CONFIG; 227 p->default_config = "ldap"; 228 } 229 230 static void 231 nss_ldap_shadow(p) 232 nss_db_params_t *p; 233 { 234 p->name = NSS_DBNAM_SHADOW; 235 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */ 236 p->flags |= NSS_USE_DEFAULT_CONFIG; 237 p->default_config = "ldap"; 238 } 239 240 241 #ifdef PAM_NIS 242 static void 243 nss_nis_passwd(p) 244 nss_db_params_t *p; 245 { 246 p->name = NSS_DBNAM_PASSWD; 247 p->flags |= NSS_USE_DEFAULT_CONFIG; 248 p->default_config = "nis"; 249 } 250 251 static void 252 nss_nis_shadow(p) 253 nss_db_params_t *p; 254 { 255 p->name = NSS_DBNAM_SHADOW; 256 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */ 257 p->flags |= NSS_USE_DEFAULT_CONFIG; 258 p->default_config = "nis"; 259 } 260 #endif /* PAM_NIS */ 261 262 static char * 263 gettok(nextpp) 264 char **nextpp; 265 { 266 char *p = *nextpp; 267 char *q = p; 268 char c; 269 270 if (p == 0) { 271 return (0); 272 } 273 while ((c = *q) != '\0' && c != ':') { 274 q++; 275 } 276 if (c == '\0') { 277 *nextpp = 0; 278 } else { 279 *q++ = '\0'; 280 *nextpp = q; 281 } 282 return (p); 283 } 284 285 /* 286 * Return values: 0 = success, 1 = parse error, 2 = erange ... 287 * The structure pointer passed in is a structure in the caller's space 288 * wherein the field pointers would be set to areas in the buffer if 289 * need be. instring and buffer should be separate areas. 290 */ 291 static int 292 str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 293 { 294 struct passwd *passwd = (struct passwd *)ent; 295 char *p, *next; 296 int black_magic; /* "+" or "-" entry */ 297 298 if (lenstr + 1 > buflen) { 299 return (NSS_STR_PARSE_ERANGE); 300 } 301 /* 302 * We copy the input string into the output buffer and 303 * operate on it in place. 304 */ 305 (void) memcpy(buffer, instr, lenstr); 306 buffer[lenstr] = '\0'; 307 308 next = buffer; 309 310 passwd->pw_name = p = gettok(&next); /* username */ 311 if (*p == '\0') { 312 /* Empty username; not allowed */ 313 return (NSS_STR_PARSE_PARSE); 314 } 315 black_magic = (*p == '+' || *p == '-'); 316 if (black_magic) { 317 passwd->pw_uid = UID_NOBODY; 318 passwd->pw_gid = GID_NOBODY; 319 /* 320 * pwconv tests pw_passwd and pw_age == NULL 321 */ 322 passwd->pw_passwd = ""; 323 passwd->pw_age = ""; 324 /* 325 * the rest of the passwd entry is "optional" 326 */ 327 passwd->pw_comment = ""; 328 passwd->pw_gecos = ""; 329 passwd->pw_dir = ""; 330 passwd->pw_shell = ""; 331 } 332 333 passwd->pw_passwd = p = gettok(&next); /* password */ 334 if (p == 0) { 335 if (black_magic) 336 return (NSS_STR_PARSE_SUCCESS); 337 else 338 return (NSS_STR_PARSE_PARSE); 339 } 340 for (; *p != '\0'; p++) { /* age */ 341 if (*p == ',') { 342 *p++ = '\0'; 343 break; 344 } 345 } 346 passwd->pw_age = p; 347 348 p = next; /* uid */ 349 if (p == 0 || *p == '\0') { 350 if (black_magic) 351 return (NSS_STR_PARSE_SUCCESS); 352 else 353 return (NSS_STR_PARSE_PARSE); 354 } 355 if (!black_magic) { 356 passwd->pw_uid = strtol(p, &next, 10); 357 if (next == p) { 358 /* uid field should be nonempty */ 359 return (NSS_STR_PARSE_PARSE); 360 } 361 /* 362 * The old code (in 2.0 thru 2.5) would check 363 * for the uid being negative, or being greater 364 * than 60001 (the rfs limit). If it met either of 365 * these conditions, the uid was translated to 60001. 366 * 367 * Now we just check for ephemeral uids; anything else 368 * is administrative policy 369 */ 370 if (passwd->pw_uid > MAXUID) 371 passwd->pw_uid = UID_NOBODY; 372 } 373 if (*next++ != ':') { 374 if (black_magic) 375 p = gettok(&next); 376 else 377 return (NSS_STR_PARSE_PARSE); 378 } 379 p = next; /* gid */ 380 if (p == 0 || *p == '\0') { 381 if (black_magic) 382 return (NSS_STR_PARSE_SUCCESS); 383 else 384 return (NSS_STR_PARSE_PARSE); 385 } 386 if (!black_magic) { 387 passwd->pw_gid = strtol(p, &next, 10); 388 if (next == p) { 389 /* gid field should be nonempty */ 390 return (NSS_STR_PARSE_PARSE); 391 } 392 /* 393 * gid should be non-negative; anything else 394 * is administrative policy. 395 */ 396 if (passwd->pw_gid > MAXUID) 397 passwd->pw_gid = GID_NOBODY; 398 } 399 if (*next++ != ':') { 400 if (black_magic) 401 p = gettok(&next); 402 else 403 return (NSS_STR_PARSE_PARSE); 404 } 405 406 passwd->pw_gecos = passwd->pw_comment = p = gettok(&next); 407 if (p == 0) { 408 if (black_magic) 409 return (NSS_STR_PARSE_SUCCESS); 410 else 411 return (NSS_STR_PARSE_PARSE); 412 } 413 414 passwd->pw_dir = p = gettok(&next); 415 if (p == 0) { 416 if (black_magic) 417 return (NSS_STR_PARSE_SUCCESS); 418 else 419 return (NSS_STR_PARSE_PARSE); 420 } 421 422 passwd->pw_shell = p = gettok(&next); 423 if (p == 0) { 424 if (black_magic) 425 return (NSS_STR_PARSE_SUCCESS); 426 else 427 return (NSS_STR_PARSE_PARSE); 428 } 429 430 /* Better not be any more fields... */ 431 if (next == 0) { 432 /* Successfully parsed and stored */ 433 return (NSS_STR_PARSE_SUCCESS); 434 } 435 return (NSS_STR_PARSE_PARSE); 436 } 437 438 typedef const char *constp; 439 440 /* 441 * Return value 1 means success and more input, 0 means error or no more 442 */ 443 static int 444 getfield(nextp, limit, uns, valp) 445 constp *nextp; 446 constp limit; 447 int uns; 448 void *valp; 449 { 450 constp p = *nextp; 451 char *endfield; 452 char numbuf[12]; /* Holds -2^31 and trailing ':' */ 453 int len; 454 long x; 455 unsigned long ux; 456 457 if (p == 0 || p >= limit) { 458 return (0); 459 } 460 if (*p == ':') { 461 p++; 462 *nextp = p; 463 return (p < limit); 464 } 465 if ((len = limit - p) > sizeof (numbuf) - 1) { 466 len = sizeof (numbuf) - 1; 467 } 468 /* 469 * We want to use strtol() and we have a readonly non-zero-terminated 470 * string, so first we copy and terminate the interesting bit. 471 * Ugh. (It's convenient to terminate with a colon rather than \0). 472 */ 473 if ((endfield = memccpy(numbuf, p, ':', len)) == 0) { 474 if (len != limit - p) { 475 /* Error -- field is too big to be a legit number */ 476 return (0); 477 } 478 numbuf[len] = ':'; 479 p = limit; 480 } else { 481 p += (endfield - numbuf); 482 } 483 if (uns) { 484 ux = strtoul(numbuf, &endfield, 10); 485 if (*endfield != ':') { 486 /* Error -- expected <integer><colon> */ 487 return (0); 488 } 489 *((unsigned int *)valp) = (unsigned int)ux; 490 } else { 491 x = strtol(numbuf, &endfield, 10); 492 if (*endfield != ':') { 493 /* Error -- expected <integer><colon> */ 494 return (0); 495 } 496 *((int *)valp) = (int)x; 497 } 498 *nextp = p; 499 return (p < limit); 500 } 501 502 /* 503 * str2spwd() -- convert a string to a shadow passwd entry. The parser is 504 * more liberal than the passwd or group parsers; since it's legitimate 505 * for almost all the fields here to be blank, the parser lets one omit 506 * any number of blank fields at the end of the entry. The acceptable 507 * forms for '+' and '-' entries are the same as those for normal entries. 508 * === Is this likely to do more harm than good? 509 * 510 * Return values: 0 = success, 1 = parse error, 2 = erange ... 511 * The structure pointer passed in is a structure in the caller's space 512 * wherein the field pointers would be set to areas in the buffer if 513 * need be. instring and buffer should be separate areas. 514 */ 515 int 516 str2spwd(instr, lenstr, ent, buffer, buflen) 517 const char *instr; 518 int lenstr; 519 void *ent; /* really (struct spwd *) */ 520 char *buffer; 521 int buflen; 522 { 523 struct spwd *shadow = (struct spwd *)ent; 524 const char *p = instr, *limit; 525 char *bufp; 526 int lencopy, black_magic; 527 528 limit = p + lenstr; 529 if ((p = memchr(instr, ':', lenstr)) == 0 || 530 ++p >= limit || 531 (p = memchr(p, ':', limit - p)) == 0) { 532 lencopy = lenstr; 533 p = 0; 534 } else { 535 lencopy = p - instr; 536 p++; 537 } 538 if (lencopy + 1 > buflen) { 539 return (NSS_STR_PARSE_ERANGE); 540 } 541 (void) memcpy(buffer, instr, lencopy); 542 buffer[lencopy] = 0; 543 544 black_magic = (*instr == '+' || *instr == '-'); 545 shadow->sp_namp = bufp = buffer; 546 shadow->sp_pwdp = 0; 547 shadow->sp_lstchg = -1; 548 shadow->sp_min = -1; 549 shadow->sp_max = -1; 550 shadow->sp_warn = -1; 551 shadow->sp_inact = -1; 552 shadow->sp_expire = -1; 553 shadow->sp_flag = 0; 554 555 if ((bufp = strchr(bufp, ':')) == 0) { 556 if (black_magic) 557 return (NSS_STR_PARSE_SUCCESS); 558 else 559 return (NSS_STR_PARSE_PARSE); 560 } 561 *bufp++ = '\0'; 562 563 shadow->sp_pwdp = bufp; 564 if (instr == 0) { 565 if ((bufp = strchr(bufp, ':')) == 0) { 566 if (black_magic) 567 return (NSS_STR_PARSE_SUCCESS); 568 else 569 return (NSS_STR_PARSE_PARSE); 570 } 571 *bufp++ = '\0'; 572 p = bufp; 573 } /* else p was set when we copied name and passwd into the buffer */ 574 575 if (!getfield(&p, limit, 0, &shadow->sp_lstchg)) 576 return (NSS_STR_PARSE_SUCCESS); 577 if (!getfield(&p, limit, 0, &shadow->sp_min)) 578 return (NSS_STR_PARSE_SUCCESS); 579 if (!getfield(&p, limit, 0, &shadow->sp_max)) 580 return (NSS_STR_PARSE_SUCCESS); 581 if (!getfield(&p, limit, 0, &shadow->sp_warn)) 582 return (NSS_STR_PARSE_SUCCESS); 583 if (!getfield(&p, limit, 0, &shadow->sp_inact)) 584 return (NSS_STR_PARSE_SUCCESS); 585 if (!getfield(&p, limit, 0, &shadow->sp_expire)) 586 return (NSS_STR_PARSE_SUCCESS); 587 if (!getfield(&p, limit, 1, &shadow->sp_flag)) 588 return (NSS_STR_PARSE_SUCCESS); 589 if (p != limit) { 590 /* Syntax error -- garbage at end of line */ 591 return (NSS_STR_PARSE_PARSE); 592 } 593 return (NSS_STR_PARSE_SUCCESS); 594 } 595 596 static nss_XbyY_buf_t *buffer; 597 static DEFINE_NSS_DB_ROOT(db_root); 598 599 #define GETBUF() \ 600 NSS_XbyY_ALLOC(&buffer, sizeof (struct passwd), NSS_BUFLEN_PASSWD) 601 602 #pragma fini(endutilpwent) 603 604 static void 605 endutilpwent(void) 606 { 607 NSS_XbyY_FREE(&buffer); 608 nss_delete(&db_root); 609 } 610 611 /*ARGSUSED*/ 612 struct passwd * 613 getpwnam_from(const char *name, pwu_repository_t *rep, int reptype) 614 { 615 nss_XbyY_buf_t *b = GETBUF(); 616 nss_XbyY_args_t arg; 617 618 if (b == 0) 619 return (0); 620 621 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd); 622 arg.key.name = name; 623 624 switch (reptype) { 625 case REP_LDAP: 626 (void) nss_search(&db_root, nss_ldap_passwd, 627 NSS_DBOP_PASSWD_BYNAME, &arg); 628 break; 629 #ifdef PAM_NIS 630 case REP_NIS: 631 (void) nss_search(&db_root, nss_nis_passwd, 632 NSS_DBOP_PASSWD_BYNAME, &arg); 633 break; 634 #endif 635 default: 636 return (NULL); 637 } 638 639 return (struct passwd *)NSS_XbyY_FINI(&arg); 640 } 641 642 /*ARGSUSED*/ 643 struct passwd * 644 getpwuid_from(uid_t uid, pwu_repository_t *rep, int reptype) 645 { 646 nss_XbyY_buf_t *b = GETBUF(); 647 nss_XbyY_args_t arg; 648 649 if (b == 0) 650 return (0); 651 652 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd); 653 arg.key.uid = uid; 654 655 switch (reptype) { 656 case REP_LDAP: 657 (void) nss_search(&db_root, nss_ldap_passwd, 658 NSS_DBOP_PASSWD_BYUID, &arg); 659 break; 660 #ifdef PAM_NIS 661 case REP_NIS: 662 (void) nss_search(&db_root, nss_nis_passwd, 663 NSS_DBOP_PASSWD_BYUID, &arg); 664 break; 665 #endif 666 default: 667 return (NULL); 668 } 669 670 return (struct passwd *)NSS_XbyY_FINI(&arg); 671 } 672 673 static nss_XbyY_buf_t *spbuf; 674 static DEFINE_NSS_DB_ROOT(spdb_root); 675 676 #define GETSPBUF() \ 677 NSS_XbyY_ALLOC(&spbuf, sizeof (struct spwd), NSS_BUFLEN_SHADOW) 678 679 #pragma fini(endutilspent) 680 681 static void 682 endutilspent(void) 683 { 684 NSS_XbyY_FREE(&spbuf); 685 nss_delete(&spdb_root); 686 } 687 688 /*ARGSUSED*/ 689 struct spwd * 690 getspnam_from(const char *name, pwu_repository_t *rep, int reptype) 691 { 692 nss_XbyY_buf_t *b = GETSPBUF(); 693 nss_XbyY_args_t arg; 694 695 if (b == 0) 696 return (0); 697 698 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2spwd); 699 arg.key.name = name; 700 switch (reptype) { 701 case REP_LDAP: 702 (void) nss_search(&spdb_root, nss_ldap_shadow, 703 NSS_DBOP_SHADOW_BYNAME, &arg); 704 break; 705 #ifdef PAM_NIS 706 case REP_NIS: 707 (void) nss_search(&spdb_root, nss_nis_shadow, 708 NSS_DBOP_SHADOW_BYNAME, &arg); 709 break; 710 #endif 711 default: 712 return (NULL); 713 } 714 return (struct spwd *)NSS_XbyY_FINI(&arg); 715 } 716