1 /* $NetBSD: yp_passwd.c,v 1.31 2005/02/26 07:19:25 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1990, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "from: @(#)local_passwd.c 8.3 (Berkeley) 4/2/94"; 36 #else 37 __RCSID("$NetBSD: yp_passwd.c,v 1.31 2005/02/26 07:19:25 thorpej Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #ifdef YP 42 43 #include <ctype.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <netdb.h> 47 #include <pwd.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <time.h> 52 #include <unistd.h> 53 #include <limits.h> 54 #include <util.h> 55 56 #include <rpc/rpc.h> 57 #include <rpcsvc/yp_prot.h> 58 #include <rpcsvc/ypclnt.h> 59 60 #include "extern.h" 61 62 #define passwd yp_passwd_rec 63 #include <rpcsvc/yppasswd.h> 64 #undef passwd 65 66 #ifndef _PASSWORD_LEN 67 #define _PASSWORD_LEN PASS_MAX 68 #endif 69 70 static uid_t uid; 71 static char *domain; 72 73 static void 74 pwerror(char *name, int err, int eval) 75 { 76 77 if (err) 78 warn("%s", name); 79 errx(eval, "NIS passwd database unchanged"); 80 } 81 82 static char * 83 getnewpasswd(struct passwd *pw, char **old_pass) 84 { 85 int tries; 86 char *p, *t; 87 static char buf[_PASSWORD_LEN+1]; 88 char salt[_PASSWORD_LEN+1]; 89 char option[LINE_MAX], *key, *opt; 90 91 (void)printf("Changing NIS password for %s.\n", pw->pw_name); 92 93 if (old_pass) { 94 *old_pass = NULL; 95 96 if (pw->pw_passwd[0]) { 97 if (strcmp(crypt(p = getpass("Old password:"), 98 pw->pw_passwd), pw->pw_passwd)) { 99 (void)printf("Sorry.\n"); 100 pwerror(NULL, 0, 1); 101 } 102 } else { 103 p = ""; 104 } 105 106 *old_pass = strdup(p); 107 if (!*old_pass) { 108 (void)printf("not enough core.\n"); 109 pwerror(NULL, 0, 1); 110 } 111 } 112 for (buf[0] = '\0', tries = 0;;) { 113 p = getpass("New password:"); 114 if (!*p) { 115 (void)printf("Password unchanged.\n"); 116 pwerror(NULL, 0, 0); 117 } 118 if (strlen(p) <= 5 && ++tries < 2) { 119 (void)printf("Please enter a longer password.\n"); 120 continue; 121 } 122 for (t = p; *t && islower((unsigned char)*t); ++t); 123 if (!*t && ++tries < 2) { 124 (void)printf("Please don't use an all-lower case " 125 "password.\nUnusual capitalization, " 126 "control characters or digits are " 127 "suggested.\n"); 128 continue; 129 } 130 (void)strlcpy(buf, p, sizeof(buf)); 131 if (!strcmp(buf, getpass("Retype new password:"))) 132 break; 133 (void)printf("Mismatch; try again, EOF to quit.\n"); 134 } 135 136 pw_getpwconf(option, sizeof(option), pw, "ypcipher"); 137 opt = option; 138 key = strsep(&opt, ","); 139 if (pw_gensalt(salt, _PASSWORD_LEN, key, opt) == -1) { 140 warn("Couldn't generate salt"); 141 pwerror(NULL, 0, 0); 142 } 143 p = strdup(crypt(buf, salt)); 144 if (!p) { 145 (void)printf("not enough core.\n"); 146 pwerror(NULL, 0, 0); 147 } 148 return (p); 149 } 150 151 static int 152 ypgetpwnam(const char *nam) 153 { 154 char *val; 155 int reason, vallen; 156 157 val = NULL; 158 reason = yp_match(domain, "passwd.byname", nam, strlen(nam), 159 &val, &vallen); 160 if (reason != 0) { 161 if (val != NULL) 162 free(val); 163 return 0; 164 } 165 free(val); 166 return 1; 167 } 168 169 #ifdef USE_PAM 170 171 void 172 pwyp_usage(const char *prefix) 173 { 174 175 (void) fprintf(stderr, "%s %s [-d nis | -y] [user]\n", 176 prefix, getprogname()); 177 } 178 179 void 180 pwyp_argv0_usage(const char *prefix) 181 { 182 183 (void) fprintf(stderr, "%s %s [user]\n", 184 prefix, getprogname()); 185 } 186 187 void 188 pwyp_process(const char *username, int argc, char **argv) 189 { 190 char *master; 191 int ch, r, rpcport, status; 192 struct yppasswd yppasswd; 193 struct passwd *pw; 194 struct timeval tv; 195 CLIENT *client; 196 197 while ((ch = getopt(argc, argv, "y")) != -1) { 198 switch (ch) { 199 case 'y': 200 /* 201 * Abosrb the -y that may have gotten us here. 202 */ 203 break; 204 205 default: 206 usage(); 207 /* NOTREACHED */ 208 } 209 } 210 211 argc -= optind; 212 argv += optind; 213 214 switch (argc) { 215 case 0: 216 /* username already provided */ 217 break; 218 case 1: 219 username = argv[0]; 220 break; 221 default: 222 usage(); 223 /* NOTREACHED */ 224 } 225 226 if (_yp_check(NULL) == 0) { 227 /* can't use YP. */ 228 errx(1, "NIS not in use."); 229 } 230 231 uid = getuid(); 232 233 /* 234 * Get local domain 235 */ 236 if ((r = yp_get_default_domain(&domain)) != 0) 237 errx(1, "can't get local NIS domain. Reason: %s", 238 yperr_string(r)); 239 240 /* 241 * Find the host for the passwd map; it should be running 242 * the daemon. 243 */ 244 if ((r = yp_master(domain, "passwd.byname", &master)) != 0) 245 errx(1, "can't find the master NIS server. Reason: %s", 246 yperr_string(r)); 247 248 /* 249 * Ask the portmapper for the port of the daemon. 250 */ 251 if ((rpcport = getrpcport(master, YPPASSWDPROG, 252 YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) 253 errx(1, "master NIS server not running yppasswd daemon.\n\t%s\n", 254 "Can't change NIS password."); 255 256 /* 257 * Be sure the port is privileged 258 */ 259 if (rpcport >= IPPORT_RESERVED) 260 errx(1, "yppasswd daemon is on an invalid port."); 261 262 /* Bail out if this is a local (non-yp) user, */ 263 /* then get user's login identity */ 264 /* XXX This should always fetch from NIS, not rely on getpwnam()! */ 265 if (!ypgetpwnam(username) || 266 !(pw = getpwnam(username))) 267 errx(1, "NIS unknown user %s", username); 268 269 if (uid && uid != pw->pw_uid) 270 errx(1, "you may only change your own password: %s", 271 strerror(EACCES)); 272 273 /* prompt for new password */ 274 yppasswd.newpw.pw_passwd = getnewpasswd(pw, &yppasswd.oldpass); 275 276 /* tell rpc.yppasswdd */ 277 yppasswd.newpw.pw_name = strdup(pw->pw_name); 278 if (!yppasswd.newpw.pw_name) { 279 err(1, "strdup"); 280 /*NOTREACHED*/ 281 } 282 yppasswd.newpw.pw_uid = pw->pw_uid; 283 yppasswd.newpw.pw_gid = pw->pw_gid; 284 yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos); 285 if (!yppasswd.newpw.pw_gecos) { 286 err(1, "strdup"); 287 /*NOTREACHED*/ 288 } 289 yppasswd.newpw.pw_dir = strdup(pw->pw_dir); 290 if (!yppasswd.newpw.pw_dir) { 291 err(1, "strdup"); 292 /*NOTREACHED*/ 293 } 294 yppasswd.newpw.pw_shell = strdup(pw->pw_shell); 295 if (!yppasswd.newpw.pw_shell) { 296 err(1, "strdup"); 297 /*NOTREACHED*/ 298 } 299 300 client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); 301 if (client == NULL) 302 errx(1, "cannot contact yppasswdd on %s: Reason: %s", 303 master, yperr_string(YPERR_YPBIND)); 304 305 client->cl_auth = authunix_create_default(); 306 tv.tv_sec = 2; 307 tv.tv_usec = 0; 308 r = clnt_call(client, YPPASSWDPROC_UPDATE, 309 xdr_yppasswd, &yppasswd, xdr_int, &status, tv); 310 if (r) 311 errx(1, "rpc to yppasswdd failed."); 312 else if (status) 313 printf("Couldn't change NIS password.\n"); 314 else 315 printf("The NIS password has been changed on %s, %s\n", 316 master, "the master NIS passwd server."); 317 } 318 319 #else /* ! USE_PAM */ 320 321 static int yflag; 322 323 int 324 yp_init(progname) 325 const char *progname; 326 { 327 int yppwd; 328 329 if (strcmp(progname, "yppasswd") == 0) { 330 yppwd = 1; 331 } else 332 yppwd = 0; 333 yflag = 0; 334 if (_yp_check(NULL) == 0) { 335 /* can't use YP. */ 336 if (yppwd) 337 errx(1, "NIS not in use."); 338 return(-1); 339 } 340 return (0); 341 } 342 343 int 344 yp_arg(ch, arg) 345 char ch; 346 const char *arg; 347 { 348 switch (ch) { 349 case 'y': 350 yflag = 1; 351 break; 352 default: 353 return(0); 354 } 355 return(1); 356 } 357 358 int 359 yp_arg_end() 360 { 361 if (yflag) 362 return (PW_USE_FORCE); 363 return (PW_USE); 364 } 365 366 void 367 yp_end() 368 { 369 /* NOOP */ 370 } 371 372 int 373 yp_chpw(username) 374 const char *username; 375 { 376 char *master; 377 int r, rpcport, status; 378 struct yppasswd yppasswd; 379 struct passwd *pw; 380 struct timeval tv; 381 CLIENT *client; 382 383 uid = getuid(); 384 385 /* 386 * Get local domain 387 */ 388 if ((r = yp_get_default_domain(&domain)) != 0) 389 errx(1, "can't get local NIS domain. Reason: %s", 390 yperr_string(r)); 391 392 /* 393 * Find the host for the passwd map; it should be running 394 * the daemon. 395 */ 396 if ((r = yp_master(domain, "passwd.byname", &master)) != 0) { 397 warnx("can't find the master NIS server. Reason: %s", 398 yperr_string(r)); 399 /* continuation */ 400 return(-1); 401 } 402 403 /* 404 * Ask the portmapper for the port of the daemon. 405 */ 406 if ((rpcport = getrpcport(master, YPPASSWDPROG, 407 YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) { 408 warnx("master NIS server not running yppasswd daemon.\n\t%s\n", 409 "Can't change NIS password."); 410 /* continuation */ 411 return(-1); 412 } 413 414 /* 415 * Be sure the port is privileged 416 */ 417 if (rpcport >= IPPORT_RESERVED) 418 errx(1, "yppasswd daemon is on an invalid port."); 419 420 /* Bail out if this is a local (non-yp) user, */ 421 /* then get user's login identity */ 422 if (!ypgetpwnam(username) || 423 !(pw = getpwnam(username))) { 424 warnx("NIS unknown user %s", username); 425 /* continuation */ 426 return(-1); 427 } 428 429 if (uid && uid != pw->pw_uid) 430 errx(1, "you may only change your own password: %s", 431 strerror(EACCES)); 432 433 /* prompt for new password */ 434 yppasswd.newpw.pw_passwd = getnewpasswd(pw, &yppasswd.oldpass); 435 436 /* tell rpc.yppasswdd */ 437 yppasswd.newpw.pw_name = strdup(pw->pw_name); 438 if (!yppasswd.newpw.pw_name) { 439 err(1, "strdup"); 440 /*NOTREACHED*/ 441 } 442 yppasswd.newpw.pw_uid = pw->pw_uid; 443 yppasswd.newpw.pw_gid = pw->pw_gid; 444 yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos); 445 if (!yppasswd.newpw.pw_gecos) { 446 err(1, "strdup"); 447 /*NOTREACHED*/ 448 } 449 yppasswd.newpw.pw_dir = strdup(pw->pw_dir); 450 if (!yppasswd.newpw.pw_dir) { 451 err(1, "strdup"); 452 /*NOTREACHED*/ 453 } 454 yppasswd.newpw.pw_shell = strdup(pw->pw_shell); 455 if (!yppasswd.newpw.pw_shell) { 456 err(1, "strdup"); 457 /*NOTREACHED*/ 458 } 459 460 client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); 461 if (client == NULL) { 462 warnx("cannot contact yppasswdd on %s: Reason: %s", 463 master, yperr_string(YPERR_YPBIND)); 464 return (YPERR_YPBIND); 465 } 466 467 client->cl_auth = authunix_create_default(); 468 tv.tv_sec = 2; 469 tv.tv_usec = 0; 470 r = clnt_call(client, YPPASSWDPROC_UPDATE, 471 xdr_yppasswd, &yppasswd, xdr_int, &status, tv); 472 if (r) 473 errx(1, "rpc to yppasswdd failed."); 474 else if (status) 475 printf("Couldn't change NIS password.\n"); 476 else 477 printf("The NIS password has been changed on %s, %s\n", 478 master, "the master NIS passwd server."); 479 return(0); 480 } 481 482 #endif /* USE_PAM */ 483 484 #endif /* YP */ 485