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