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