Home | History | Annotate | Line # | Download | only in passwd
      1 /*	$NetBSD: yp_passwd.c,v 1.38 2024/05/18 19:28:36 andvar 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.38 2024/05/18 19:28:36 andvar 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 __dead static void
     74 pwerror(const char *name, int show_err, int eval)
     75 {
     76 
     77 	if (show_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 	const char *p, *t;
     87 	char *result;
     88 	static char buf[_PASSWORD_LEN + 1];
     89 	char salt[_PASSWORD_LEN + 1];
     90 	char option[LINE_MAX], *key, *opt;
     91 
     92 	(void)printf("Changing NIS password for %s.\n", pw->pw_name);
     93 
     94 	if (old_pass) {
     95 		*old_pass = NULL;
     96 
     97 		if (pw->pw_passwd[0]) {
     98 			if (strcmp(crypt(p = getpass("Old password:"),
     99 			    pw->pw_passwd),  pw->pw_passwd)) {
    100 				(void)printf("Sorry.\n");
    101 				pwerror(NULL, 0, 1);
    102 			}
    103 		} else {
    104 			p = "";
    105 		}
    106 
    107 		*old_pass = strdup(p);
    108 		if (!*old_pass) {
    109 			(void)printf("not enough core.\n");
    110 			pwerror(NULL, 0, 1);
    111 		}
    112 	}
    113 	for (buf[0] = '\0', tries = 0;;) {
    114 		p = getpass("New password:");
    115 		if (!*p) {
    116 			(void)printf("Password unchanged.\n");
    117 			pwerror(NULL, 0, 0);
    118 		}
    119 		if (strlen(p) <= 5 && ++tries < 2) {
    120 			(void)printf("Please enter a longer password.\n");
    121 			continue;
    122 		}
    123 		for (t = p; *t && islower((unsigned char)*t); ++t);
    124 		if (!*t && ++tries < 2) {
    125 			(void)printf("Please don't use an all-lower case "
    126 			    "password.\nUnusual capitalization, "
    127 			    "control characters or digits are "
    128 			    "suggested.\n");
    129 			continue;
    130 		}
    131 		(void)strlcpy(buf, p, sizeof(buf));
    132 		if (!strcmp(buf, getpass("Retype new password:")))
    133 			break;
    134 		(void)printf("Mismatch; try again, EOF to quit.\n");
    135 	}
    136 
    137 	pw_getpwconf(option, sizeof(option), pw, "ypcipher");
    138 	opt = option;
    139 	key = strsep(&opt, ",");
    140 	if (pw_gensalt(salt, _PASSWORD_LEN, key, opt) == -1) {
    141 		warn("Couldn't generate salt");
    142 		pwerror(NULL, 0, 0);
    143 	}
    144 	result = strdup(crypt(buf, salt));
    145 	if (!result) {
    146 		(void)printf("not enough core.\n");
    147 		pwerror(NULL, 0, 0);
    148 	}
    149 	return result;
    150 }
    151 
    152 static void
    153 makeypp(struct yppasswd *ypp, struct passwd *pw)
    154 {
    155 	/* prompt for new password */
    156 	ypp->newpw.pw_passwd = getnewpasswd(pw, &ypp->oldpass);
    157 
    158 	/* tell rpc.yppasswdd */
    159 	ypp->newpw.pw_name	= estrdup(pw->pw_name);
    160 	ypp->newpw.pw_uid 	= pw->pw_uid;
    161 	ypp->newpw.pw_gid	= pw->pw_gid;
    162 	ypp->newpw.pw_gecos	= estrdup(pw->pw_gecos);
    163 	ypp->newpw.pw_dir	= estrdup(pw->pw_dir);
    164 	ypp->newpw.pw_shell	= estrdup(pw->pw_shell);
    165 }
    166 
    167 static int
    168 ypgetpwnam(const char *nam, struct passwd *pwd)
    169 {
    170 	char *val;
    171 	int reason, vallen, namlen = (int)strlen(nam);
    172 	int flags;
    173 	int ok;
    174 
    175 	flags = ok = 0;
    176 	val = NULL;
    177 	reason = yp_match(domain, "master.passwd.byname", nam, namlen,
    178 	    &val, &vallen);
    179 	if (reason == YPERR_MAP) {
    180 		reason = yp_match(domain, "passwd.byname", nam, namlen,
    181 		    &val, &vallen);
    182 		flags = _PASSWORD_OLDFMT;
    183 	}
    184 	if (reason != 0)
    185 		goto out;
    186 
    187 	if (pw_scan(val, pwd, &flags) == 0)
    188 		goto out;
    189 
    190 	ok = 1;
    191 	val = NULL;	/* Don't free the memory, it is still in use */
    192 out:
    193 	if (val)
    194 		free(val);
    195 	return ok;
    196 }
    197 
    198 #ifdef USE_PAM
    199 
    200 void
    201 pwyp_usage(const char *prefix)
    202 {
    203 
    204 	(void)fprintf(stderr, "%s %s [-d nis | -y] [user]\n",
    205 	    prefix, getprogname());
    206 }
    207 
    208 void
    209 pwyp_argv0_usage(const char *prefix)
    210 {
    211 
    212 	(void)fprintf(stderr, "%s %s [user]\n",
    213 	    prefix, getprogname());
    214 }
    215 
    216 void
    217 pwyp_process(const char *username, int argc, char **argv)
    218 {
    219 	char *master;
    220 	int ch, r, rpcport, status;
    221 	enum clnt_stat yr;
    222 	struct yppasswd ypp;
    223 	struct passwd pwb, pwb2, *pw;
    224 	char pwbuf[1024];
    225 	struct timeval tv;
    226 	CLIENT *client;
    227 
    228 	while ((ch = getopt(argc, argv, "y")) != -1) {
    229 		switch (ch) {
    230 		case 'y':
    231 			/*
    232 			 * Absorb the -y that may have gotten us here.
    233 			 */
    234 			break;
    235 
    236 		default:
    237 			usage();
    238 			/* NOTREACHED */
    239 		}
    240 	}
    241 
    242 	argc -= optind;
    243 	argv += optind;
    244 
    245 	switch (argc) {
    246 	case 0:
    247 		/* username already provided */
    248 		break;
    249 	case 1:
    250 		username = argv[0];
    251 		break;
    252 	default:
    253 		usage();
    254 		/*NOTREACHED*/
    255 	}
    256 
    257 	if (_yp_check(NULL) == 0) {
    258 		/* can't use YP. */
    259 		errx(EXIT_FAILURE, "NIS not in use.");
    260 	}
    261 
    262 	uid = getuid();
    263 
    264 	/*
    265 	 * Get local domain
    266 	 */
    267 	if ((r = yp_get_default_domain(&domain)) != 0)
    268 		errx(EXIT_FAILURE, "Can't get local NIS domain (%s)",
    269 		    yperr_string(r));
    270 
    271 	/*
    272 	 * Find the host for the passwd map; it should be running
    273 	 * the daemon.
    274 	 */
    275 	if ((r = yp_master(domain, "passwd.byname", &master)) != 0)
    276 		errx(EXIT_FAILURE, "Can't find the master NIS server (%s)",
    277 		    yperr_string(r));
    278 
    279 	/*
    280 	 * Ask the portmapper for the port of the daemon.
    281 	 */
    282 	if ((rpcport = getrpcport(master, YPPASSWDPROG,
    283 	    YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0)
    284 		errx(EXIT_FAILURE, "Master NIS server not running yppasswd "
    285 		    "daemon");
    286 
    287 	/*
    288 	 * Be sure the port is privileged
    289 	 */
    290 	if (rpcport >= IPPORT_RESERVED)
    291 		errx(EXIT_FAILURE, "Yppasswd daemon is on an invalid port");
    292 
    293 	/* Bail out if this is a local (non-yp) user, */
    294 	/* then get user's login identity */
    295 	if (!ypgetpwnam(username, &pwb) ||
    296 	    getpwnam_r(username, &pwb2, pwbuf, sizeof(pwbuf), &pw) ||
    297 	    pw == NULL)
    298 		errx(EXIT_FAILURE, "NIS unknown user %s", username);
    299 
    300 	if (uid && uid != pwb.pw_uid) {
    301 		errno = EACCES;
    302 		err(EXIT_FAILURE, "You may only change your own password");
    303 	}
    304 
    305 	makeypp(&ypp, &pwb);
    306 
    307 	client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
    308 	if (client == NULL)
    309 		errx(EXIT_FAILURE, "Cannot contact yppasswdd on %s (%s)",
    310 		    master, yperr_string(YPERR_YPBIND));
    311 
    312 	client->cl_auth = authunix_create_default();
    313 	tv.tv_sec = 2;
    314 	tv.tv_usec = 0;
    315 	yr = clnt_call(client, YPPASSWDPROC_UPDATE,
    316 	    xdr_yppasswd, &ypp, xdr_int, &status, tv);
    317 	if (yr != RPC_SUCCESS)
    318 		errx(EXIT_FAILURE, "RPC to yppasswdd failed (%s)",
    319 		    clnt_sperrno(yr));
    320 	else if (status)
    321 		printf("Couldn't change NIS password.\n");
    322 	else
    323 		printf("The NIS password has been changed on %s, %s\n",
    324 		    master, "the master NIS passwd server.");
    325 }
    326 
    327 #else /* ! USE_PAM */
    328 
    329 static	int yflag;
    330 
    331 int
    332 yp_init(const char *progname)
    333 {
    334 	int yppwd;
    335 
    336 	if (strcmp(progname, "yppasswd") == 0) {
    337 		yppwd = 1;
    338 	} else
    339 		yppwd = 0;
    340 	yflag = 0;
    341 	if (_yp_check(NULL) == 0) {
    342 		/* can't use YP. */
    343 		if (yppwd)
    344 			errx(EXIT_FAILURE, "NIS not in use");
    345 		return -1;
    346 	}
    347 	return 0;
    348 }
    349 
    350 int
    351 yp_arg(char ch, const char *arg)
    352 {
    353 	switch (ch) {
    354 	case 'y':
    355 		yflag = 1;
    356 		break;
    357 	default:
    358 		return 0;
    359 	}
    360 	return 1;
    361 }
    362 
    363 int
    364 yp_arg_end(void)
    365 {
    366 	if (yflag)
    367 		return PW_USE_FORCE;
    368 	return PW_USE;
    369 }
    370 
    371 void
    372 yp_end(void)
    373 {
    374 	/* NOOP */
    375 }
    376 
    377 int
    378 yp_chpw(const char *username)
    379 {
    380 	char *master;
    381 	int r, rpcport, status;
    382 	enum clnt_stat yr;
    383 	struct yppasswd ypp;
    384 	struct passwd *pw, pwb;
    385 	char pwbuf[1024];
    386 	struct timeval tv;
    387 	CLIENT *client;
    388 
    389 	uid = getuid();
    390 
    391 	/*
    392 	 * Get local domain
    393 	 */
    394 	if ((r = yp_get_default_domain(&domain)) != 0)
    395 		errx(EXIT_FAILURE, "can't get local NIS domain.  Reason: %s",
    396 		    yperr_string(r));
    397 
    398 	/*
    399 	 * Find the host for the passwd map; it should be running
    400 	 * the daemon.
    401 	 */
    402 	if ((r = yp_master(domain, "passwd.byname", &master)) != 0) {
    403 		warnx("can't find the master NIS server.  Reason: %s",
    404 		    yperr_string(r));
    405 		/* continuation */
    406 		return -1;
    407 	}
    408 
    409 	/*
    410 	 * Ask the portmapper for the port of the daemon.
    411 	 */
    412 	if ((rpcport = getrpcport(master, YPPASSWDPROG,
    413 	    YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) {
    414 		warnx("Master NIS server not running yppasswd daemon");
    415 		/* continuation */
    416 		return -1;
    417 	}
    418 
    419 	/*
    420 	 * Be sure the port is privileged
    421 	 */
    422 	if (rpcport >= IPPORT_RESERVED)
    423 		errx(EXIT_FAILURE, "Yppasswd daemon is on an invalid port");
    424 
    425 	/* Bail out if this is a local (non-yp) user, */
    426 	/* then get user's login identity */
    427 	if (!ypgetpwnam(username, pw = &pwb) ||
    428 	    getpwnam_r(username, &pwb, pwbuf, sizeof(pwbuf), &pw) ||
    429 	    pw == NULL) {
    430 		warnx("NIS unknown user %s", username);
    431 		/* continuation */
    432 		return -1;
    433 	}
    434 
    435 	if (uid && uid != pw->pw_uid) {
    436 		errno = EACCES;
    437 		err(EXIT_FAILURE, "You may only change your own password");
    438 	}
    439 
    440 	makeypp(&ypp, pw);
    441 
    442 	client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
    443 	if (client == NULL) {
    444 		warnx("Cannot contact yppasswdd on %s (%s)",
    445 		    master, yperr_string(YPERR_YPBIND));
    446 		return YPERR_YPBIND;
    447 	}
    448 
    449 	client->cl_auth = authunix_create_default();
    450 	tv.tv_sec = 2;
    451 	tv.tv_usec = 0;
    452 	yr = clnt_call(client, YPPASSWDPROC_UPDATE,
    453 	    xdr_yppasswd, &ypp, xdr_int, &status, tv);
    454 	if (yr != RPC_SUCCESS)
    455 		errx(EXIT_FAILURE, "RPC to yppasswdd failed (%s)",
    456 		    clnt_sperrno(yr));
    457 	else if (status)
    458 		printf("Couldn't change NIS password.\n");
    459 	else
    460 		printf("The NIS password has been changed on %s, %s\n",
    461 		    master, "the master NIS passwd server.");
    462 	return 0;
    463 }
    464 
    465 #endif /* USE_PAM */
    466 
    467 #endif	/* YP */
    468