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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 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 static struct passwd *nisplus_getpw_from_master(const char *, char *); 46 static struct spwd *nisplus_getsp_from_master(const char *, char *); 47 48 /* 49 * name_to_int(rep) 50 * 51 * Translate the repository to a bitmask. 52 * if we don't recognise the repository name, we return REP_ERANGE 53 */ 54 int 55 name_to_int(char *rep_name) 56 { 57 int result = REP_ERANGE; 58 59 if (strcmp(rep_name, "files") == 0) 60 result = REP_FILES; 61 else if (strcmp(rep_name, "nis") == 0) 62 result = REP_NIS; 63 else if (strcmp(rep_name, "nisplus") == 0) 64 result = REP_NISPLUS; 65 else if (strcmp(rep_name, "ldap") == 0) 66 result = REP_LDAP; 67 else if (strcmp(rep_name, "compat") == 0) { 68 struct __nsw_switchconfig *cfg; 69 enum __nsw_parse_err pserr; 70 71 cfg = __nsw_getconfig("passwd_compat", &pserr); 72 if (cfg == NULL) { 73 result = REP_FILES | REP_NIS; 74 } else { 75 if (strcmp(cfg->lookups->service_name, "nisplus") == 0) 76 result = REP_FILES | REP_NISPLUS; 77 else if (strcmp(cfg->lookups->service_name, "ldap") == 78 0) 79 result = REP_FILES | REP_LDAP; 80 else 81 result = REP_ERANGE; 82 __nsw_freeconfig(cfg); 83 } 84 } 85 86 return (result); 87 } 88 89 /* 90 * Figure out which repository we use in compat mode. 91 */ 92 int 93 get_compat_mode(void) 94 { 95 struct __nsw_switchconfig *cfg; 96 enum __nsw_parse_err pserr; 97 int result = REP_COMPAT_NIS; 98 99 if ((cfg = __nsw_getconfig("passwd_compat", &pserr)) != NULL) { 100 if (strcmp(cfg->lookups->service_name, "nisplus") == 0) 101 result = REP_COMPAT_NISPLUS; 102 else if (strcmp(cfg->lookups->service_name, "ldap") == 0) 103 result = REP_COMPAT_LDAP; 104 } 105 __nsw_freeconfig(cfg); 106 107 return (result); 108 } 109 110 /* 111 * get_ns(rep, accesstype) 112 * 113 * returns a bitmask of repositories to use based on either 114 * 1. the repository that is given as argument 115 * 2. the nsswitch.conf file 116 * 3. the type of access requested 117 * 118 * "accesstype" indicates whether we are reading from or writing to the 119 * repository. We need to know this since "compat" will translate into 120 * REP_NSS (the nss-switch) for READ access (needed to decode 121 * the black-magic '+' entries) but it translates into a bitmask 122 * on WRITE access. 123 * 124 * If we detect read-access in compat mode, we augment the result 125 * with one of REP_COMPAT_{NIS,NISPLUS,LDAP}. We need this in order to 126 * implement ATTR_REP_NAME in nss_getpwnam. 127 * 128 * A return value of REP_NOREP indicates an error. 129 */ 130 int 131 get_ns(pwu_repository_t *rep, int accesstype) 132 { 133 struct __nsw_switchconfig *conf = NULL; 134 enum __nsw_parse_err pserr; 135 struct __nsw_lookup *lkp; 136 struct __nsw_lookup *lkp2; 137 int result = REP_NOREP; 138 139 if (rep != PWU_DEFAULT_REP) { 140 result = name_to_int(rep->type); 141 return (result); 142 } 143 144 conf = __nsw_getconfig("passwd", &pserr); 145 if (conf == NULL) { 146 /* 147 * No config found. The user didn't supply a repository, 148 * so we try to change the password in the default 149 * repositories (files and nis) even though we cannot 150 * find the name service switch entry. (Backward compat) 151 */ 152 syslog(LOG_ERR, "passwdutil.so: nameservice switch entry for " 153 "passwd not found."); 154 result = REP_FILES | REP_NIS; 155 return (result); 156 } 157 158 lkp = conf->lookups; 159 160 /* 161 * Supported nsswitch.conf can have a maximum of 2 repositories. 162 * If we encounter an unsupported nsswitch.conf, we return REP_NSS 163 * to fall back to the nsswitch backend. 164 */ 165 if (conf->num_lookups == 1) { 166 /* files or compat */ 167 168 if (strcmp(lkp->service_name, "files") == 0) { 169 result = name_to_int(lkp->service_name); 170 } else if (strcmp(lkp->service_name, "compat") == 0) { 171 if (accesstype == PWU_READ) 172 result = REP_NSS | get_compat_mode(); 173 else 174 result = name_to_int(lkp->service_name); 175 } else 176 result = REP_NSS; 177 178 } else if (conf->num_lookups == 2) { 179 lkp2 = lkp->next; 180 if (strcmp(lkp->service_name, "files") == 0) { 181 result = REP_FILES; 182 if (strcmp(lkp2->service_name, "ldap") == 0) 183 result |= REP_LDAP; 184 else if (strcmp(lkp2->service_name, "nis") == 0) 185 result |= REP_NIS; 186 else if (strcmp(lkp2->service_name, "nisplus") == 0) 187 result |= REP_NISPLUS; 188 else 189 result = REP_NSS; 190 } else { 191 result = REP_NSS; 192 } 193 } else { 194 result = REP_NSS; 195 } 196 197 __nsw_freeconfig(conf); 198 return (result); 199 } 200 201 static void 202 nss_ldap_passwd(p) 203 nss_db_params_t *p; 204 { 205 p->name = NSS_DBNAM_PASSWD; 206 p->flags |= NSS_USE_DEFAULT_CONFIG; 207 p->default_config = "ldap"; 208 } 209 210 static void 211 nss_ldap_shadow(p) 212 nss_db_params_t *p; 213 { 214 p->name = NSS_DBNAM_SHADOW; 215 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */ 216 p->flags |= NSS_USE_DEFAULT_CONFIG; 217 p->default_config = "ldap"; 218 } 219 220 221 #ifdef PAM_NIS 222 static void 223 nss_nis_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 = "nis"; 229 } 230 231 static void 232 nss_nis_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 = "nis"; 239 } 240 #endif /* PAM_NIS */ 241 242 243 static void 244 nss_nisplus_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 = "nisplus"; 250 } 251 252 static void 253 nss_nisplus_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 = "nisplus"; 260 } 261 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 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 case REP_NISPLUS: 630 if (rep && rep->scope) 631 return (nisplus_getpw_from_master(name, rep->scope)); 632 633 (void) nss_search(&db_root, nss_nisplus_passwd, 634 NSS_DBOP_PASSWD_BYNAME, &arg); 635 break; 636 #ifdef PAM_NIS 637 case REP_NIS: 638 (void) nss_search(&db_root, nss_nis_passwd, 639 NSS_DBOP_PASSWD_BYNAME, &arg); 640 break; 641 #endif 642 default: 643 return (NULL); 644 } 645 646 return (struct passwd *)NSS_XbyY_FINI(&arg); 647 } 648 649 /*ARGSUSED*/ 650 struct passwd * 651 getpwuid_from(uid_t uid, pwu_repository_t *rep, int reptype) 652 { 653 nss_XbyY_buf_t *b = GETBUF(); 654 nss_XbyY_args_t arg; 655 656 if (b == 0) 657 return (0); 658 659 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd); 660 arg.key.uid = uid; 661 662 switch (reptype) { 663 case REP_LDAP: 664 (void) nss_search(&db_root, nss_ldap_passwd, 665 NSS_DBOP_PASSWD_BYUID, &arg); 666 break; 667 case REP_NISPLUS: 668 (void) nss_search(&db_root, nss_nisplus_passwd, 669 NSS_DBOP_PASSWD_BYUID, &arg); 670 break; 671 #ifdef PAM_NIS 672 case REP_NIS: 673 (void) nss_search(&db_root, nss_nis_passwd, 674 NSS_DBOP_PASSWD_BYUID, &arg); 675 break; 676 #endif 677 default: 678 return (NULL); 679 } 680 681 return (struct passwd *)NSS_XbyY_FINI(&arg); 682 } 683 684 static nss_XbyY_buf_t *spbuf; 685 static DEFINE_NSS_DB_ROOT(spdb_root); 686 687 #define GETSPBUF() \ 688 NSS_XbyY_ALLOC(&spbuf, sizeof (struct spwd), NSS_BUFLEN_SHADOW) 689 690 #pragma fini(endutilspent) 691 692 static void 693 endutilspent(void) 694 { 695 NSS_XbyY_FREE(&spbuf); 696 nss_delete(&spdb_root); 697 } 698 699 struct spwd * 700 getspnam_from(const char *name, pwu_repository_t *rep, int reptype) 701 { 702 nss_XbyY_buf_t *b = GETSPBUF(); 703 nss_XbyY_args_t arg; 704 705 if (b == 0) 706 return (0); 707 708 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2spwd); 709 arg.key.name = name; 710 switch (reptype) { 711 case REP_LDAP: 712 (void) nss_search(&spdb_root, nss_ldap_shadow, 713 NSS_DBOP_SHADOW_BYNAME, &arg); 714 break; 715 case REP_NISPLUS: 716 if (rep && rep->scope) 717 return (nisplus_getsp_from_master(name, rep->scope)); 718 719 (void) nss_search(&spdb_root, nss_nisplus_shadow, 720 NSS_DBOP_SHADOW_BYNAME, &arg); 721 break; 722 #ifdef PAM_NIS 723 case REP_NIS: 724 (void) nss_search(&spdb_root, nss_nis_shadow, 725 NSS_DBOP_SHADOW_BYNAME, &arg); 726 break; 727 #endif 728 default: 729 return (NULL); 730 } 731 return (struct spwd *)NSS_XbyY_FINI(&arg); 732 } 733 734 735 static nis_result * 736 nisplus_match(const char *name, char *domain, char *buf, int len) 737 { 738 int n; 739 int flags; 740 nis_result *res; 741 nis_object *object; 742 743 n = snprintf(buf, len, "[name=%s],passwd.org_dir.%s", name, domain); 744 if (n >= len) { 745 syslog(LOG_ERR, "nisplus_match: name too long"); 746 return (NULL); 747 } 748 if (buf[n-1] != '.') { 749 if (n == len-1) { 750 syslog(LOG_ERR, "nisplus_match: name too long"); 751 return (NULL); 752 } 753 buf[n++] = '.'; 754 buf[n] = '\0'; 755 } 756 757 flags = USE_DGRAM | FOLLOW_LINKS | FOLLOW_PATH | MASTER_ONLY; 758 759 res = nis_list(buf, flags, NULL, NULL); 760 761 if (res == NULL) { 762 syslog(LOG_ERR, "nisplus_match: nis_list returned NULL"); 763 return (NULL); 764 } 765 766 if (NIS_RES_STATUS(res) != NIS_SUCCESS || NIS_RES_NUMOBJ(res) != 1) { 767 syslog(LOG_ERR, "nisplus_match: match failed: %s", 768 nis_sperrno(NIS_RES_STATUS(res))); 769 nis_freeresult(res); 770 return (NULL); 771 } 772 773 object = NIS_RES_OBJECT(res); 774 775 if (object->EN_data.en_cols.en_cols_len < 8) { 776 syslog(LOG_ERR, "nisplus_match: " 777 "not a valid passwd table entry for user %s", name); 778 nis_freeresult(res); 779 return (NULL); 780 } 781 782 return (res); 783 } 784 785 #define SAFE_STRDUP(dst, idx) \ 786 if ((idx) <= 3 && ENTRY_VAL(nret, (idx)) == NULL) { \ 787 syslog(LOG_ERR, \ 788 "passwdutil: missing field from password entry"); \ 789 goto error; \ 790 } \ 791 len = ENTRY_LEN(nret, (idx)); \ 792 (dst) = malloc(len+1); \ 793 if ((dst) == NULL) { \ 794 syslog(LOG_ERR, "passwdutil: out of memory"); \ 795 goto error; \ 796 } \ 797 (dst)[len] = '\0'; \ 798 (void) strncpy((dst), \ 799 ENTRY_VAL(nret, (idx)) ? ENTRY_VAL(nret, (idx)) : "", \ 800 len); 801 802 803 804 static struct passwd * 805 nisplus_getpw_from_master(const char *name, char *domain) 806 { 807 char lookup[NIS_MAXNAMELEN+1]; 808 nis_result *res; 809 nis_object *nret; 810 int len; 811 char *p; 812 struct passwd *pw; 813 814 if ((pw = calloc(1, sizeof (*pw))) == NULL) 815 return (NULL); 816 817 res = nisplus_match(name, domain, lookup, sizeof (lookup)); 818 819 if (res == NULL) 820 return (NULL); 821 822 nret = NIS_RES_OBJECT(res); 823 824 SAFE_STRDUP(pw->pw_name, 0); 825 826 if ((pw->pw_passwd = strdup("x")) == NULL) { 827 syslog(LOG_ERR, "passwdutil: out of memory"); 828 goto error; 829 } 830 831 SAFE_STRDUP(p, 2); 832 pw->pw_uid = atoi(p); 833 free(p); 834 835 SAFE_STRDUP(p, 3); 836 pw->pw_gid = atoi(p); 837 free(p); 838 839 pw->pw_age = NULL; 840 pw->pw_comment = NULL; 841 842 /*CONSTANTCONDITION*/ 843 SAFE_STRDUP(pw->pw_gecos, 4); 844 845 /*CONSTANTCONDITION*/ 846 SAFE_STRDUP(pw->pw_dir, 5); 847 848 /*CONSTANTCONDITION*/ 849 SAFE_STRDUP(pw->pw_shell, 6); 850 851 nis_freeresult(res); 852 853 return (pw); 854 855 error: 856 nis_freeresult(res); 857 if (pw->pw_name) 858 free(pw->pw_name); 859 if (pw->pw_passwd) 860 free(pw->pw_passwd); 861 if (pw->pw_gecos) 862 free(pw->pw_gecos); 863 if (pw->pw_dir) 864 free(pw->pw_dir); 865 if (pw->pw_shell) 866 free(pw->pw_shell); 867 free(pw); 868 869 return (NULL); 870 } 871 872 /* 873 * struct spwd * nisplus_getsp_from_master() 874 * 875 * Get the shadow structure from a NIS+ master. 876 * This routine normally runs with EUID==0. This can cause trouble 877 * if the NIS+ tables are locked down so that only the owner can 878 * access the encrypted password. If we detect that scenario, we switch 879 * EUID to the owner of the record and refetch it. 880 */ 881 static struct spwd * 882 nisplus_getsp_from_master(const char *name, char *domain) 883 { 884 char lookup[NIS_MAXNAMELEN+1]; 885 nis_result *res = NULL; 886 nis_object *nret = NULL; 887 int len; 888 struct spwd *spw; 889 char *shadow = NULL; 890 const char *p = NULL; 891 const char *limit; 892 893 res = nisplus_match(name, domain, lookup, sizeof (lookup)); 894 if (res == NULL) 895 return (NULL); 896 897 nret = NIS_RES_OBJECT(res); 898 899 /*CONSTANTCONDITION*/ 900 SAFE_STRDUP(shadow, 7); 901 902 /* 903 * If we got NOPWDRTR as password, try again with EUID set 904 * to the UID of the record-owner. 905 */ 906 if (strncmp(shadow, NOPWDRTR, 4) == 0) { 907 char *p; 908 uid_t owner_uid; 909 uid_t euid = geteuid(); 910 911 SAFE_STRDUP(p, 2); /* record-owner field */ 912 owner_uid = atoi(p); 913 free(p); 914 915 if (owner_uid != euid) { 916 /* re-obtain entry using owners EUID */ 917 free(shadow); 918 nis_freeresult(res); 919 920 (void) seteuid(owner_uid); 921 res = nisplus_match(name, domain, lookup, 922 sizeof (lookup)); 923 (void) seteuid(euid); 924 925 if (res == NULL) 926 return (NULL); 927 nret = NIS_RES_OBJECT(res); 928 929 /*CONSTANTCONDITION*/ 930 SAFE_STRDUP(shadow, 7); 931 } 932 } 933 934 if ((spw = calloc(1, sizeof (*spw))) == NULL) { 935 nis_freeresult(res); 936 return (NULL); 937 } 938 939 SAFE_STRDUP(spw->sp_namp, 0); 940 SAFE_STRDUP(spw->sp_pwdp, 1); 941 942 nis_freeresult(res); 943 944 limit = shadow + strlen(shadow) + 1; 945 p = shadow; 946 947 spw->sp_lstchg = -1; 948 spw->sp_min = -1; 949 spw->sp_max = -1; 950 spw->sp_warn = -1; 951 spw->sp_inact = -1; 952 spw->sp_expire = -1; 953 spw->sp_flag = 0; 954 955 if (!getfield(&p, limit, 0, &spw->sp_lstchg)) 956 goto out; 957 958 if (!getfield(&p, limit, 0, &spw->sp_min)) 959 goto out; 960 961 if (!getfield(&p, limit, 0, &spw->sp_max)) 962 goto out; 963 964 if (!getfield(&p, limit, 0, &spw->sp_warn)) 965 goto out; 966 967 if (!getfield(&p, limit, 0, &spw->sp_inact)) 968 goto out; 969 970 if (!getfield(&p, limit, 0, &spw->sp_expire)) 971 goto out; 972 973 if (!getfield(&p, limit, 1, &spw->sp_flag)) 974 goto out; 975 976 if (p != limit) { 977 syslog(LOG_ERR, "passwdutil: garbage at end of record"); 978 goto error; 979 } 980 981 out: 982 free(shadow); 983 return (spw); 984 985 error: 986 if (spw->sp_namp) 987 free(spw->sp_namp); 988 if (spw->sp_pwdp) 989 free(spw->sp_pwdp); 990 free(spw); 991 if (shadow) 992 free(shadow); 993 return (NULL); 994 } 995