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 <sys/types.h> 29 #include <nsswitch.h> 30 #include <stdlib.h> 31 #include <stdio.h> 32 #include <string.h> 33 #include <syslog.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 37 #include "ns_sldap.h" 38 #include <nss_dbdefs.h> 39 #include <nsswitch.h> 40 #include <pwd.h> 41 #include <shadow.h> 42 #include <rpcsvc/nis.h> 43 44 #include "passwdutil.h" 45 46 static struct passwd *nisplus_getpw_from_master(const char *, char *); 47 static struct spwd *nisplus_getsp_from_master(const char *, char *); 48 49 /* 50 * name_to_int(rep) 51 * 52 * Translate the repository to a bitmask. 53 * if we don't recognise the repository name, we return REP_ERANGE 54 */ 55 int 56 name_to_int(char *rep_name) 57 { 58 int result = REP_ERANGE; 59 60 if (strcmp(rep_name, "files") == 0) 61 result = REP_FILES; 62 else if (strcmp(rep_name, "nis") == 0) 63 result = REP_NIS; 64 else if (strcmp(rep_name, "nisplus") == 0) 65 result = REP_NISPLUS; 66 else if (strcmp(rep_name, "ldap") == 0) 67 result = REP_LDAP; 68 else if (strcmp(rep_name, "compat") == 0) { 69 struct __nsw_switchconfig *cfg; 70 enum __nsw_parse_err pserr; 71 72 cfg = __nsw_getconfig("passwd_compat", &pserr); 73 if (cfg == NULL) { 74 result = REP_FILES | REP_NIS; 75 } else { 76 if (strcmp(cfg->lookups->service_name, "nisplus") == 0) 77 result = REP_FILES | REP_NISPLUS; 78 else if (strcmp(cfg->lookups->service_name, "ldap") == 79 0) 80 result = REP_FILES | REP_LDAP; 81 else 82 result = REP_ERANGE; 83 __nsw_freeconfig(cfg); 84 } 85 } 86 87 return (result); 88 } 89 90 /* 91 * Figure out which repository we use in compat mode. 92 */ 93 int 94 get_compat_mode(void) 95 { 96 struct __nsw_switchconfig *cfg; 97 enum __nsw_parse_err pserr; 98 int result = REP_COMPAT_NIS; 99 100 if ((cfg = __nsw_getconfig("passwd_compat", &pserr)) != NULL) { 101 if (strcmp(cfg->lookups->service_name, "nisplus") == 0) 102 result = REP_COMPAT_NISPLUS; 103 else if (strcmp(cfg->lookups->service_name, "ldap") == 0) 104 result = REP_COMPAT_LDAP; 105 } 106 __nsw_freeconfig(cfg); 107 108 return (result); 109 } 110 111 /* 112 * get_ns(rep, accesstype) 113 * 114 * returns a bitmask of repositories to use based on either 115 * 1. the repository that is given as argument 116 * 2. the nsswitch.conf file 117 * 3. the type of access requested 118 * 119 * "accesstype" indicates whether we are reading from or writing to the 120 * repository. We need to know this since "compat" will translate into 121 * REP_NSS (the nss-switch) for READ access (needed to decode 122 * the black-magic '+' entries) but it translates into a bitmask 123 * on WRITE access. 124 * 125 * If we detect read-access in compat mode, we augment the result 126 * with one of REP_COMPAT_{NIS,NISPLUS,LDAP}. We need this in order to 127 * implement ATTR_REP_NAME in nss_getpwnam. 128 * 129 * A return value of REP_NOREP indicates an error. 130 */ 131 int 132 get_ns(pwu_repository_t *rep, int accesstype) 133 { 134 struct __nsw_switchconfig *conf = NULL; 135 enum __nsw_parse_err pserr; 136 struct __nsw_lookup *lkp; 137 struct __nsw_lookup *lkp2; 138 int result = REP_NOREP; 139 140 if (rep != PWU_DEFAULT_REP) { 141 result = name_to_int(rep->type); 142 return (result); 143 } 144 145 conf = __nsw_getconfig("passwd", &pserr); 146 if (conf == NULL) { 147 /* 148 * No config found. The user didn't supply a repository, 149 * so we try to change the password in the default 150 * repositories (files and nis) even though we cannot 151 * find the name service switch entry. (Backward compat) 152 */ 153 syslog(LOG_ERR, "passwdutil.so: nameservice switch entry for " 154 "passwd not found."); 155 result = REP_FILES | REP_NIS; 156 return (result); 157 } 158 159 lkp = conf->lookups; 160 161 /* 162 * Supported nsswitch.conf can have a maximum of 2 repositories. 163 * If we encounter an unsupported nsswitch.conf, we return REP_NSS 164 * to fall back to the nsswitch backend. 165 */ 166 if (conf->num_lookups == 1) { 167 /* files or compat */ 168 169 if (strcmp(lkp->service_name, "files") == 0) { 170 result = name_to_int(lkp->service_name); 171 } else if (strcmp(lkp->service_name, "compat") == 0) { 172 if (accesstype == PWU_READ) 173 result = REP_NSS | get_compat_mode(); 174 else 175 result = name_to_int(lkp->service_name); 176 } else 177 result = REP_NSS; 178 179 } else if (conf->num_lookups == 2) { 180 lkp2 = lkp->next; 181 if (strcmp(lkp->service_name, "files") == 0) { 182 result = REP_FILES; 183 if (strcmp(lkp2->service_name, "ldap") == 0) 184 result |= REP_LDAP; 185 else if (strcmp(lkp2->service_name, "nis") == 0) 186 result |= REP_NIS; 187 else if (strcmp(lkp2->service_name, "nisplus") == 0) 188 result |= REP_NISPLUS; 189 else 190 result = REP_NSS; 191 } else { 192 result = REP_NSS; 193 } 194 } else { 195 result = REP_NSS; 196 } 197 198 __nsw_freeconfig(conf); 199 return (result); 200 } 201 202 static void 203 nss_ldap_passwd(p) 204 nss_db_params_t *p; 205 { 206 p->name = NSS_DBNAM_PASSWD; 207 p->flags |= NSS_USE_DEFAULT_CONFIG; 208 p->default_config = "ldap"; 209 } 210 211 static void 212 nss_ldap_shadow(p) 213 nss_db_params_t *p; 214 { 215 p->name = NSS_DBNAM_SHADOW; 216 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */ 217 p->flags |= NSS_USE_DEFAULT_CONFIG; 218 p->default_config = "ldap"; 219 } 220 221 222 #ifdef PAM_NIS 223 static void 224 nss_nis_passwd(p) 225 nss_db_params_t *p; 226 { 227 p->name = NSS_DBNAM_PASSWD; 228 p->flags |= NSS_USE_DEFAULT_CONFIG; 229 p->default_config = "nis"; 230 } 231 232 static void 233 nss_nis_shadow(p) 234 nss_db_params_t *p; 235 { 236 p->name = NSS_DBNAM_SHADOW; 237 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */ 238 p->flags |= NSS_USE_DEFAULT_CONFIG; 239 p->default_config = "nis"; 240 } 241 #endif /* PAM_NIS */ 242 243 244 static void 245 nss_nisplus_passwd(p) 246 nss_db_params_t *p; 247 { 248 p->name = NSS_DBNAM_PASSWD; 249 p->flags |= NSS_USE_DEFAULT_CONFIG; 250 p->default_config = "nisplus"; 251 } 252 253 static void 254 nss_nisplus_shadow(p) 255 nss_db_params_t *p; 256 { 257 p->name = NSS_DBNAM_SHADOW; 258 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */ 259 p->flags |= NSS_USE_DEFAULT_CONFIG; 260 p->default_config = "nisplus"; 261 } 262 263 264 static char * 265 gettok(nextpp) 266 char **nextpp; 267 { 268 char *p = *nextpp; 269 char *q = p; 270 char c; 271 272 if (p == 0) { 273 return (0); 274 } 275 while ((c = *q) != '\0' && c != ':') { 276 q++; 277 } 278 if (c == '\0') { 279 *nextpp = 0; 280 } else { 281 *q++ = '\0'; 282 *nextpp = q; 283 } 284 return (p); 285 } 286 287 /* 288 * Return values: 0 = success, 1 = parse error, 2 = erange ... 289 * The structure pointer passed in is a structure in the caller's space 290 * wherein the field pointers would be set to areas in the buffer if 291 * need be. instring and buffer should be separate areas. 292 */ 293 static int 294 str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 295 { 296 struct passwd *passwd = (struct passwd *)ent; 297 char *p, *next; 298 int black_magic; /* "+" or "-" entry */ 299 300 if (lenstr + 1 > buflen) { 301 return (NSS_STR_PARSE_ERANGE); 302 } 303 /* 304 * We copy the input string into the output buffer and 305 * operate on it in place. 306 */ 307 (void) memcpy(buffer, instr, lenstr); 308 buffer[lenstr] = '\0'; 309 310 next = buffer; 311 312 passwd->pw_name = p = gettok(&next); /* username */ 313 if (*p == '\0') { 314 /* Empty username; not allowed */ 315 return (NSS_STR_PARSE_PARSE); 316 } 317 black_magic = (*p == '+' || *p == '-'); 318 if (black_magic) { 319 passwd->pw_uid = UID_NOBODY; 320 passwd->pw_gid = GID_NOBODY; 321 /* 322 * pwconv tests pw_passwd and pw_age == NULL 323 */ 324 passwd->pw_passwd = ""; 325 passwd->pw_age = ""; 326 /* 327 * the rest of the passwd entry is "optional" 328 */ 329 passwd->pw_comment = ""; 330 passwd->pw_gecos = ""; 331 passwd->pw_dir = ""; 332 passwd->pw_shell = ""; 333 } 334 335 passwd->pw_passwd = p = gettok(&next); /* password */ 336 if (p == 0) { 337 if (black_magic) 338 return (NSS_STR_PARSE_SUCCESS); 339 else 340 return (NSS_STR_PARSE_PARSE); 341 } 342 for (; *p != '\0'; p++) { /* age */ 343 if (*p == ',') { 344 *p++ = '\0'; 345 break; 346 } 347 } 348 passwd->pw_age = p; 349 350 p = next; /* uid */ 351 if (p == 0 || *p == '\0') { 352 if (black_magic) 353 return (NSS_STR_PARSE_SUCCESS); 354 else 355 return (NSS_STR_PARSE_PARSE); 356 } 357 if (!black_magic) { 358 passwd->pw_uid = strtol(p, &next, 10); 359 if (next == p) { 360 /* uid field should be nonempty */ 361 return (NSS_STR_PARSE_PARSE); 362 } 363 /* 364 * The old code (in 2.0 thru 2.5) would check 365 * for the uid being negative, or being greater 366 * than 60001 (the rfs limit). If it met either of 367 * these conditions, the uid was translated to 60001. 368 * 369 * Now we just check for ephemeral uids; anything else 370 * is administrative policy 371 */ 372 if (passwd->pw_uid > MAXUID) 373 passwd->pw_uid = UID_NOBODY; 374 } 375 if (*next++ != ':') { 376 if (black_magic) 377 p = gettok(&next); 378 else 379 return (NSS_STR_PARSE_PARSE); 380 } 381 p = next; /* gid */ 382 if (p == 0 || *p == '\0') { 383 if (black_magic) 384 return (NSS_STR_PARSE_SUCCESS); 385 else 386 return (NSS_STR_PARSE_PARSE); 387 } 388 if (!black_magic) { 389 passwd->pw_gid = strtol(p, &next, 10); 390 if (next == p) { 391 /* gid field should be nonempty */ 392 return (NSS_STR_PARSE_PARSE); 393 } 394 /* 395 * gid should be non-negative; anything else 396 * is administrative policy. 397 */ 398 if (passwd->pw_gid > MAXUID) 399 passwd->pw_gid = GID_NOBODY; 400 } 401 if (*next++ != ':') { 402 if (black_magic) 403 p = gettok(&next); 404 else 405 return (NSS_STR_PARSE_PARSE); 406 } 407 408 passwd->pw_gecos = passwd->pw_comment = p = gettok(&next); 409 if (p == 0) { 410 if (black_magic) 411 return (NSS_STR_PARSE_SUCCESS); 412 else 413 return (NSS_STR_PARSE_PARSE); 414 } 415 416 passwd->pw_dir = p = gettok(&next); 417 if (p == 0) { 418 if (black_magic) 419 return (NSS_STR_PARSE_SUCCESS); 420 else 421 return (NSS_STR_PARSE_PARSE); 422 } 423 424 passwd->pw_shell = p = gettok(&next); 425 if (p == 0) { 426 if (black_magic) 427 return (NSS_STR_PARSE_SUCCESS); 428 else 429 return (NSS_STR_PARSE_PARSE); 430 } 431 432 /* Better not be any more fields... */ 433 if (next == 0) { 434 /* Successfully parsed and stored */ 435 return (NSS_STR_PARSE_SUCCESS); 436 } 437 return (NSS_STR_PARSE_PARSE); 438 } 439 440 typedef const char *constp; 441 442 /* 443 * Return value 1 means success and more input, 0 means error or no more 444 */ 445 static int 446 getfield(nextp, limit, uns, valp) 447 constp *nextp; 448 constp limit; 449 int uns; 450 void *valp; 451 { 452 constp p = *nextp; 453 char *endfield; 454 char numbuf[12]; /* Holds -2^31 and trailing ':' */ 455 int len; 456 long x; 457 unsigned long ux; 458 459 if (p == 0 || p >= limit) { 460 return (0); 461 } 462 if (*p == ':') { 463 p++; 464 *nextp = p; 465 return (p < limit); 466 } 467 if ((len = limit - p) > sizeof (numbuf) - 1) { 468 len = sizeof (numbuf) - 1; 469 } 470 /* 471 * We want to use strtol() and we have a readonly non-zero-terminated 472 * string, so first we copy and terminate the interesting bit. 473 * Ugh. (It's convenient to terminate with a colon rather than \0). 474 */ 475 if ((endfield = memccpy(numbuf, p, ':', len)) == 0) { 476 if (len != limit - p) { 477 /* Error -- field is too big to be a legit number */ 478 return (0); 479 } 480 numbuf[len] = ':'; 481 p = limit; 482 } else { 483 p += (endfield - numbuf); 484 } 485 if (uns) { 486 ux = strtoul(numbuf, &endfield, 10); 487 if (*endfield != ':') { 488 /* Error -- expected <integer><colon> */ 489 return (0); 490 } 491 *((unsigned int *)valp) = (unsigned int)ux; 492 } else { 493 x = strtol(numbuf, &endfield, 10); 494 if (*endfield != ':') { 495 /* Error -- expected <integer><colon> */ 496 return (0); 497 } 498 *((int *)valp) = (int)x; 499 } 500 *nextp = p; 501 return (p < limit); 502 } 503 504 /* 505 * str2spwd() -- convert a string to a shadow passwd entry. The parser is 506 * more liberal than the passwd or group parsers; since it's legitimate 507 * for almost all the fields here to be blank, the parser lets one omit 508 * any number of blank fields at the end of the entry. The acceptable 509 * forms for '+' and '-' entries are the same as those for normal entries. 510 * === Is this likely to do more harm than good? 511 * 512 * Return values: 0 = success, 1 = parse error, 2 = erange ... 513 * The structure pointer passed in is a structure in the caller's space 514 * wherein the field pointers would be set to areas in the buffer if 515 * need be. instring and buffer should be separate areas. 516 */ 517 int 518 str2spwd(instr, lenstr, ent, buffer, buflen) 519 const char *instr; 520 int lenstr; 521 void *ent; /* really (struct spwd *) */ 522 char *buffer; 523 int buflen; 524 { 525 struct spwd *shadow = (struct spwd *)ent; 526 const char *p = instr, *limit; 527 char *bufp; 528 int lencopy, black_magic; 529 530 limit = p + lenstr; 531 if ((p = memchr(instr, ':', lenstr)) == 0 || 532 ++p >= limit || 533 (p = memchr(p, ':', limit - p)) == 0) { 534 lencopy = lenstr; 535 p = 0; 536 } else { 537 lencopy = p - instr; 538 p++; 539 } 540 if (lencopy + 1 > buflen) { 541 return (NSS_STR_PARSE_ERANGE); 542 } 543 (void) memcpy(buffer, instr, lencopy); 544 buffer[lencopy] = 0; 545 546 black_magic = (*instr == '+' || *instr == '-'); 547 shadow->sp_namp = bufp = buffer; 548 shadow->sp_pwdp = 0; 549 shadow->sp_lstchg = -1; 550 shadow->sp_min = -1; 551 shadow->sp_max = -1; 552 shadow->sp_warn = -1; 553 shadow->sp_inact = -1; 554 shadow->sp_expire = -1; 555 shadow->sp_flag = 0; 556 557 if ((bufp = strchr(bufp, ':')) == 0) { 558 if (black_magic) 559 return (NSS_STR_PARSE_SUCCESS); 560 else 561 return (NSS_STR_PARSE_PARSE); 562 } 563 *bufp++ = '\0'; 564 565 shadow->sp_pwdp = bufp; 566 if (instr == 0) { 567 if ((bufp = strchr(bufp, ':')) == 0) { 568 if (black_magic) 569 return (NSS_STR_PARSE_SUCCESS); 570 else 571 return (NSS_STR_PARSE_PARSE); 572 } 573 *bufp++ = '\0'; 574 p = bufp; 575 } /* else p was set when we copied name and passwd into the buffer */ 576 577 if (!getfield(&p, limit, 0, &shadow->sp_lstchg)) 578 return (NSS_STR_PARSE_SUCCESS); 579 if (!getfield(&p, limit, 0, &shadow->sp_min)) 580 return (NSS_STR_PARSE_SUCCESS); 581 if (!getfield(&p, limit, 0, &shadow->sp_max)) 582 return (NSS_STR_PARSE_SUCCESS); 583 if (!getfield(&p, limit, 0, &shadow->sp_warn)) 584 return (NSS_STR_PARSE_SUCCESS); 585 if (!getfield(&p, limit, 0, &shadow->sp_inact)) 586 return (NSS_STR_PARSE_SUCCESS); 587 if (!getfield(&p, limit, 0, &shadow->sp_expire)) 588 return (NSS_STR_PARSE_SUCCESS); 589 if (!getfield(&p, limit, 1, &shadow->sp_flag)) 590 return (NSS_STR_PARSE_SUCCESS); 591 if (p != limit) { 592 /* Syntax error -- garbage at end of line */ 593 return (NSS_STR_PARSE_PARSE); 594 } 595 return (NSS_STR_PARSE_SUCCESS); 596 } 597 598 static nss_XbyY_buf_t *buffer; 599 static DEFINE_NSS_DB_ROOT(db_root); 600 601 #define GETBUF() \ 602 NSS_XbyY_ALLOC(&buffer, sizeof (struct passwd), NSS_BUFLEN_PASSWD) 603 604 #pragma fini(endutilpwent) 605 606 static void 607 endutilpwent(void) 608 { 609 NSS_XbyY_FREE(&buffer); 610 nss_delete(&db_root); 611 } 612 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 case REP_NISPLUS: 631 if (rep && rep->scope) 632 return (nisplus_getpw_from_master(name, rep->scope)); 633 634 (void) nss_search(&db_root, nss_nisplus_passwd, 635 NSS_DBOP_PASSWD_BYNAME, &arg); 636 break; 637 #ifdef PAM_NIS 638 case REP_NIS: 639 (void) nss_search(&db_root, nss_nis_passwd, 640 NSS_DBOP_PASSWD_BYNAME, &arg); 641 break; 642 #endif 643 default: 644 return (NULL); 645 } 646 647 return (struct passwd *)NSS_XbyY_FINI(&arg); 648 } 649 650 /*ARGSUSED*/ 651 struct passwd * 652 getpwuid_from(uid_t uid, pwu_repository_t *rep, int reptype) 653 { 654 nss_XbyY_buf_t *b = GETBUF(); 655 nss_XbyY_args_t arg; 656 657 if (b == 0) 658 return (0); 659 660 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd); 661 arg.key.uid = uid; 662 663 switch (reptype) { 664 case REP_LDAP: 665 (void) nss_search(&db_root, nss_ldap_passwd, 666 NSS_DBOP_PASSWD_BYUID, &arg); 667 break; 668 case REP_NISPLUS: 669 (void) nss_search(&db_root, nss_nisplus_passwd, 670 NSS_DBOP_PASSWD_BYUID, &arg); 671 break; 672 #ifdef PAM_NIS 673 case REP_NIS: 674 (void) nss_search(&db_root, nss_nis_passwd, 675 NSS_DBOP_PASSWD_BYUID, &arg); 676 break; 677 #endif 678 default: 679 return (NULL); 680 } 681 682 return (struct passwd *)NSS_XbyY_FINI(&arg); 683 } 684 685 static nss_XbyY_buf_t *spbuf; 686 static DEFINE_NSS_DB_ROOT(spdb_root); 687 688 #define GETSPBUF() \ 689 NSS_XbyY_ALLOC(&spbuf, sizeof (struct spwd), NSS_BUFLEN_SHADOW) 690 691 #pragma fini(endutilspent) 692 693 static void 694 endutilspent(void) 695 { 696 NSS_XbyY_FREE(&spbuf); 697 nss_delete(&spdb_root); 698 } 699 700 struct spwd * 701 getspnam_from(const char *name, pwu_repository_t *rep, int reptype) 702 { 703 nss_XbyY_buf_t *b = GETSPBUF(); 704 nss_XbyY_args_t arg; 705 706 if (b == 0) 707 return (0); 708 709 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2spwd); 710 arg.key.name = name; 711 switch (reptype) { 712 case REP_LDAP: 713 (void) nss_search(&spdb_root, nss_ldap_shadow, 714 NSS_DBOP_SHADOW_BYNAME, &arg); 715 break; 716 case REP_NISPLUS: 717 if (rep && rep->scope) 718 return (nisplus_getsp_from_master(name, rep->scope)); 719 720 (void) nss_search(&spdb_root, nss_nisplus_shadow, 721 NSS_DBOP_SHADOW_BYNAME, &arg); 722 break; 723 #ifdef PAM_NIS 724 case REP_NIS: 725 (void) nss_search(&spdb_root, nss_nis_shadow, 726 NSS_DBOP_SHADOW_BYNAME, &arg); 727 break; 728 #endif 729 default: 730 return (NULL); 731 } 732 return (struct spwd *)NSS_XbyY_FINI(&arg); 733 } 734 735 736 static nis_result * 737 nisplus_match(const char *name, char *domain, char *buf, int len) 738 { 739 int n; 740 int flags; 741 nis_result *res; 742 nis_object *object; 743 744 n = snprintf(buf, len, "[name=%s],passwd.org_dir.%s", name, domain); 745 if (n >= len) { 746 syslog(LOG_ERR, "nisplus_match: name too long"); 747 return (NULL); 748 } 749 if (buf[n-1] != '.') { 750 if (n == len-1) { 751 syslog(LOG_ERR, "nisplus_match: name too long"); 752 return (NULL); 753 } 754 buf[n++] = '.'; 755 buf[n] = '\0'; 756 } 757 758 flags = USE_DGRAM | FOLLOW_LINKS | FOLLOW_PATH | MASTER_ONLY; 759 760 res = nis_list(buf, flags, NULL, NULL); 761 762 if (res == NULL) { 763 syslog(LOG_ERR, "nisplus_match: nis_list returned NULL"); 764 return (NULL); 765 } 766 767 if (NIS_RES_STATUS(res) != NIS_SUCCESS || NIS_RES_NUMOBJ(res) != 1) { 768 syslog(LOG_ERR, "nisplus_match: match failed: %s", 769 nis_sperrno(NIS_RES_STATUS(res))); 770 nis_freeresult(res); 771 return (NULL); 772 } 773 774 object = NIS_RES_OBJECT(res); 775 776 if (object->EN_data.en_cols.en_cols_len < 8) { 777 syslog(LOG_ERR, "nisplus_match: " 778 "not a valid passwd table entry for user %s", name); 779 nis_freeresult(res); 780 return (NULL); 781 } 782 783 return (res); 784 } 785 786 #define SAFE_STRDUP(dst, idx) \ 787 if ((idx) <= 3 && ENTRY_VAL(nret, (idx)) == NULL) { \ 788 syslog(LOG_ERR, \ 789 "passwdutil: missing field from password entry"); \ 790 goto error; \ 791 } \ 792 len = ENTRY_LEN(nret, (idx)); \ 793 (dst) = malloc(len+1); \ 794 if ((dst) == NULL) { \ 795 syslog(LOG_ERR, "passwdutil: out of memory"); \ 796 goto error; \ 797 } \ 798 (dst)[len] = '\0'; \ 799 (void) strncpy((dst), \ 800 ENTRY_VAL(nret, (idx)) ? ENTRY_VAL(nret, (idx)) : "", \ 801 len); 802 803 804 805 static struct passwd * 806 nisplus_getpw_from_master(const char *name, char *domain) 807 { 808 char lookup[NIS_MAXNAMELEN+1]; 809 nis_result *res; 810 nis_object *nret; 811 int len; 812 char *p; 813 struct passwd *pw; 814 815 if ((pw = calloc(1, sizeof (*pw))) == NULL) 816 return (NULL); 817 818 res = nisplus_match(name, domain, lookup, sizeof (lookup)); 819 820 if (res == NULL) 821 return (NULL); 822 823 nret = NIS_RES_OBJECT(res); 824 825 SAFE_STRDUP(pw->pw_name, 0); 826 827 if ((pw->pw_passwd = strdup("x")) == NULL) { 828 syslog(LOG_ERR, "passwdutil: out of memory"); 829 goto error; 830 } 831 832 SAFE_STRDUP(p, 2); 833 pw->pw_uid = atoi(p); 834 free(p); 835 836 SAFE_STRDUP(p, 3); 837 pw->pw_gid = atoi(p); 838 free(p); 839 840 pw->pw_age = NULL; 841 pw->pw_comment = NULL; 842 843 /*CONSTANTCONDITION*/ 844 SAFE_STRDUP(pw->pw_gecos, 4); 845 846 /*CONSTANTCONDITION*/ 847 SAFE_STRDUP(pw->pw_dir, 5); 848 849 /*CONSTANTCONDITION*/ 850 SAFE_STRDUP(pw->pw_shell, 6); 851 852 nis_freeresult(res); 853 854 return (pw); 855 856 error: 857 nis_freeresult(res); 858 if (pw->pw_name) 859 free(pw->pw_name); 860 if (pw->pw_passwd) 861 free(pw->pw_passwd); 862 if (pw->pw_gecos) 863 free(pw->pw_gecos); 864 if (pw->pw_dir) 865 free(pw->pw_dir); 866 if (pw->pw_shell) 867 free(pw->pw_shell); 868 free(pw); 869 870 return (NULL); 871 } 872 873 /* 874 * struct spwd * nisplus_getsp_from_master() 875 * 876 * Get the shadow structure from a NIS+ master. 877 * This routine normally runs with EUID==0. This can cause trouble 878 * if the NIS+ tables are locked down so that only the owner can 879 * access the encrypted password. If we detect that scenario, we switch 880 * EUID to the owner of the record and refetch it. 881 */ 882 static struct spwd * 883 nisplus_getsp_from_master(const char *name, char *domain) 884 { 885 char lookup[NIS_MAXNAMELEN+1]; 886 nis_result *res = NULL; 887 nis_object *nret = NULL; 888 int len; 889 struct spwd *spw; 890 char *shadow = NULL; 891 const char *p = NULL; 892 const char *limit; 893 894 res = nisplus_match(name, domain, lookup, sizeof (lookup)); 895 if (res == NULL) 896 return (NULL); 897 898 nret = NIS_RES_OBJECT(res); 899 900 /*CONSTANTCONDITION*/ 901 SAFE_STRDUP(shadow, 7); 902 903 /* 904 * If we got "*NP*" as password, try again with EUID set to 905 * the UID of the record-owner. 906 */ 907 if (strncmp(shadow, "*NP*", 4) == 0) { 908 char *p; 909 uid_t owner_uid; 910 uid_t euid = geteuid(); 911 912 SAFE_STRDUP(p, 2); /* record-owner field */ 913 owner_uid = atoi(p); 914 free(p); 915 916 if (owner_uid != euid) { 917 /* re-obtain entry using owners EUID */ 918 free(shadow); 919 nis_freeresult(res); 920 921 (void) seteuid(owner_uid); 922 res = nisplus_match(name, domain, lookup, 923 sizeof (lookup)); 924 (void) seteuid(euid); 925 926 if (res == NULL) 927 return (NULL); 928 nret = NIS_RES_OBJECT(res); 929 930 /*CONSTANTCONDITION*/ 931 SAFE_STRDUP(shadow, 7); 932 } 933 } 934 935 if ((spw = calloc(1, sizeof (*spw))) == NULL) { 936 nis_freeresult(res); 937 return (NULL); 938 } 939 940 SAFE_STRDUP(spw->sp_namp, 0); 941 SAFE_STRDUP(spw->sp_pwdp, 1); 942 943 nis_freeresult(res); 944 945 limit = shadow + strlen(shadow) + 1; 946 p = shadow; 947 948 spw->sp_lstchg = -1; 949 spw->sp_min = -1; 950 spw->sp_max = -1; 951 spw->sp_warn = -1; 952 spw->sp_inact = -1; 953 spw->sp_expire = -1; 954 spw->sp_flag = 0; 955 956 if (!getfield(&p, limit, 0, &spw->sp_lstchg)) 957 goto out; 958 959 if (!getfield(&p, limit, 0, &spw->sp_min)) 960 goto out; 961 962 if (!getfield(&p, limit, 0, &spw->sp_max)) 963 goto out; 964 965 if (!getfield(&p, limit, 0, &spw->sp_warn)) 966 goto out; 967 968 if (!getfield(&p, limit, 0, &spw->sp_inact)) 969 goto out; 970 971 if (!getfield(&p, limit, 0, &spw->sp_expire)) 972 goto out; 973 974 if (!getfield(&p, limit, 1, &spw->sp_flag)) 975 goto out; 976 977 if (p != limit) { 978 syslog(LOG_ERR, "passwdutil: garbage at end of record"); 979 goto error; 980 } 981 982 out: 983 free(shadow); 984 return (spw); 985 986 error: 987 if (spw->sp_namp) 988 free(spw->sp_namp); 989 if (spw->sp_pwdp) 990 free(spw->sp_pwdp); 991 free(spw); 992 if (shadow) 993 free(shadow); 994 return (NULL); 995 } 996