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