194719Sdes/*-
294719Sdes * Copyright (c) 2002 Networks Associates Technologies, Inc.
394719Sdes * All rights reserved.
41590Srgrimes *
594719Sdes * This software was developed for the FreeBSD Project by ThinkSec AS and
694719Sdes * NAI Labs, the Security Research Division of Network Associates, Inc.
794719Sdes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
894719Sdes * DARPA CHATS research program.
994719Sdes *
101590Srgrimes * Redistribution and use in source and binary forms, with or without
111590Srgrimes * modification, are permitted provided that the following conditions
121590Srgrimes * are met:
131590Srgrimes * 1. Redistributions of source code must retain the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer.
151590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161590Srgrimes *    notice, this list of conditions and the following disclaimer in the
171590Srgrimes *    documentation and/or other materials provided with the distribution.
1894719Sdes * 3. The name of the author may not be used to endorse or promote
1994719Sdes *    products derived from this software without specific prior written
2094719Sdes *    permission.
211590Srgrimes *
2294719Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
231590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2594719Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
261590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321590Srgrimes * SUCH DAMAGE.
331590Srgrimes */
341590Srgrimes
3595627Smarkm#include <sys/cdefs.h>
3695627Smarkm__FBSDID("$FreeBSD$");
3795627Smarkm
3894719Sdes#include <sys/param.h>
391590Srgrimes
401590Srgrimes#include <err.h>
4194719Sdes#include <pwd.h>
421590Srgrimes#include <stdio.h>
4396384Sjedgar#include <stdlib.h>
44200462Sdelphij#include <syslog.h>
451590Srgrimes#include <unistd.h>
461590Srgrimes
4794719Sdes#include <security/pam_appl.h>
4894719Sdes#include <security/openpam.h>
496067Swpaul
5094719Sdesstatic pam_handle_t *pamh;
5194719Sdesstatic struct pam_conv pamc = {
5294719Sdes	openpam_ttyconv,
5394719Sdes	NULL
5494719Sdes};
555752Swollman
5694719Sdesstatic char	*yp_domain;
5794719Sdesstatic char	*yp_host;
581590Srgrimes
5994719Sdesstatic void
6094719Sdesusage(void)
6194719Sdes{
6295258Sdes	fprintf(stderr, "usage: passwd [-ly] [-d domain] [-h host] [user]\n");
6394719Sdes	exit(1);
6494719Sdes}
651590Srgrimes
661590Srgrimesint
6794719Sdesmain(int argc, char *argv[])
681590Srgrimes{
6994719Sdes	char hostname[MAXHOSTNAMELEN];
70201385Sed	struct passwd *pwd = NULL; /* Keep compiler happy. */
7194719Sdes	int o, pam_err;
7294719Sdes	uid_t uid;
731590Srgrimes
7494719Sdes	while ((o = getopt(argc, argv, "d:h:loy")) != -1)
7594719Sdes		switch (o) {
7694719Sdes		case 'd':
7794719Sdes			yp_domain = optarg;
781590Srgrimes			break;
7994719Sdes		case 'h':
8094719Sdes			yp_host = optarg;
815752Swollman			break;
8294719Sdes		case 'l':
8314212Swpaul		case 'o':
8494719Sdes		case 'y':
8594719Sdes			/* compatibility */
8614212Swpaul			break;
871590Srgrimes		default:
881590Srgrimes			usage();
891590Srgrimes		}
901590Srgrimes
911590Srgrimes	argc -= optind;
921590Srgrimes	argv += optind;
931590Srgrimes
9494719Sdes	uid = getuid();
951590Srgrimes
9694719Sdes	switch (argc) {
971590Srgrimes	case 0:
9894719Sdes		if ((pwd = getpwuid(uid)) == NULL)
9994719Sdes			errx(1, "who are you?");
1001590Srgrimes		break;
1011590Srgrimes	case 1:
10294719Sdes		if ((pwd = getpwnam(*argv)) == NULL)
10394719Sdes			errx(1, "%s: no such user", *argv);
1041590Srgrimes		break;
1051590Srgrimes	default:
1061590Srgrimes		usage();
1071590Srgrimes	}
1081590Srgrimes
10994719Sdes	if (uid != 0 && uid != pwd->pw_uid)
11094719Sdes		errx(1, "permission denied");
1116067Swpaul
11294719Sdes	/* check where the user's from */
11394719Sdes	switch (pwd->pw_fields & _PWF_SOURCE) {
11494719Sdes	case _PWF_FILES:
11594719Sdes		fprintf(stderr, "Changing local password for %s\n",
11694719Sdes		    pwd->pw_name);
11794719Sdes		break;
11894719Sdes	case _PWF_NIS:
11994719Sdes		fprintf(stderr, "Changing NIS password for %s\n",
12094719Sdes		    pwd->pw_name);
12194719Sdes		break;
12294719Sdes	default:
123113693Snectar		/* XXX: Green men ought to be supported via PAM. */
124113693Snectar		errx(1,
125113693Snectar	  "Sorry, `passwd' can only change passwords for local or NIS users.");
1265752Swollman	}
1271590Srgrimes
12894719Sdes#define pam_check(func) do { \
12994719Sdes	if (pam_err != PAM_SUCCESS) { \
13094719Sdes		if (pam_err == PAM_AUTH_ERR || pam_err == PAM_PERM_DENIED || \
13194719Sdes		    pam_err == PAM_AUTHTOK_RECOVERY_ERR) \
13294719Sdes			warnx("sorry"); \
13394719Sdes		else \
13494719Sdes			warnx("%s(): %s", func, pam_strerror(pamh, pam_err)); \
13594719Sdes		goto end; \
13694719Sdes	} \
13794719Sdes} while (0)
1381590Srgrimes
13994719Sdes	/* initialize PAM */
14094719Sdes	pam_err = pam_start("passwd", pwd->pw_name, &pamc, &pamh);
14194719Sdes	pam_check("pam_start");
14294719Sdes
14394719Sdes	pam_err = pam_set_item(pamh, PAM_TTY, ttyname(STDERR_FILENO));
14494719Sdes	pam_check("pam_set_item");
14594719Sdes	gethostname(hostname, sizeof hostname);
14694719Sdes	pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
14794719Sdes	pam_check("pam_set_item");
14894719Sdes	pam_err = pam_set_item(pamh, PAM_RUSER, getlogin());
14994719Sdes	pam_check("pam_set_item");
15094719Sdes
15194719Sdes	/* set YP domain and host */
15294719Sdes	pam_err = pam_set_data(pamh, "yp_domain", yp_domain, NULL);
15394719Sdes	pam_check("pam_set_data");
15494719Sdes	pam_err = pam_set_data(pamh, "yp_server", yp_host, NULL);
15594719Sdes	pam_check("pam_set_data");
15694719Sdes
15794719Sdes	/* set new password */
15894719Sdes	pam_err = pam_chauthtok(pamh, 0);
15994719Sdes	pam_check("pam_chauthtok");
16094719Sdes
16194719Sdes end:
16294719Sdes	pam_end(pamh, pam_err);
16394719Sdes	exit(pam_err == PAM_SUCCESS ? 0 : 1);
1641590Srgrimes}
165