1 /*	$OpenBSD: login_chpass.c,v 1.2 2001/10/24 13:06:35 mpech Exp $	*/
2 
3 /*-
4  * Copyright (c) 1995,1996 Berkeley Software Design, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Berkeley Software Design,
17  *      Inc.
18  * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
19  *    or promote products derived from this software without specific prior
20  *    written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	BSDI $From: login_chpass.c,v 1.3 1996/08/21 21:01:48 prb Exp $
35  */
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/resource.h>
40 #include <sys/file.h>
41 #include <sys/wait.h>
42 
43 #include <err.h>
44 #include <errno.h>
45 #include <pwd.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <syslog.h>
51 #include <unistd.h>
52 #include <login_cap.h>
53 
54 #ifdef YP
55 # include <netdb.h>
56 # include <rpc/rpc.h>
57 # include <rpcsvc/yp_prot.h>
58 # include <rpcsvc/ypclnt.h>
59 # define passwd yp_passwd_rec
60 # include <rpcsvc/yppasswd.h>
61 # undef passwd
62 #endif
63 
64 #ifdef KERBEROS
65 # include <netinet/in.h>
66 # include <kerberosIV/krb.h>
67 # include <kerberosIV/kadm.h>
68 # include <kerberosIV/kadm_err.h>
69 #endif
70 
71 #define	_PATH_LOGIN_LCHPASS	"/usr/libexec/auth/login_lchpass"
72 
73 #ifdef  YP
74 int	_yp_check __P((char **));
75 char	*ypgetnewpasswd __P((struct passwd *, char **));
76 struct passwd *ypgetpwnam __P((char *));
77 #endif
78 
79 #ifdef KERBEROS
80 int	get_pw_new_pwd __P((char *, int, krb_principal *, int));
81 char	realm[REALM_SZ];
82 #endif
83 
84 void	local_chpass __P((char **));
85 void	krb_chpass __P((char *, char *, char **));
86 void	yp_chpass __P((char *));
87 
88 int
89 main(argc, argv)
90 	int argc;
91 	char *argv[];
92 {
93     	struct rlimit rl;
94     	char *username;
95     	char *instance;
96     	int c;
97 
98 	rl.rlim_cur = 0;
99 	rl.rlim_max = 0;
100 	(void)setrlimit(RLIMIT_CORE, &rl);
101 
102 	(void)signal(SIGQUIT, SIG_IGN);
103 	(void)signal(SIGINT, SIG_IGN);
104 	(void)setpriority(PRIO_PROCESS, 0, 0);
105 
106 	openlog("login", LOG_ODELAY, LOG_AUTH);
107 
108     	while ((c = getopt(argc, argv, "s:v:")) != -1)
109 		switch(c) {
110 		case 'v':
111 			break;
112 		case 's':	/* service */
113 			if (strcmp(optarg, "login") != 0) {
114 				syslog(LOG_ERR, "%s: invalid service", optarg);
115 				exit(1);
116 			}
117 			break;
118 		default:
119 			syslog(LOG_ERR, "usage error");
120 			exit(1);
121 		}
122 
123 	switch(argc - optind) {
124 	case 2:
125 		/* class is not used */
126 	case 1:
127 		username = argv[optind];
128 		break;
129 	default:
130 		syslog(LOG_ERR, "usage error");
131 		exit(1);
132 	}
133 
134 	/* Instance ignored for all but Kerberos. */
135 	instance = strchr(username, '.');
136 	if (instance)
137 		*instance++ = '\0';
138 	else
139 		instance = "";
140 
141 #ifdef KERBEROS
142 	if (krb_get_lrealm(realm, 0) == KSUCCESS)
143 		krb_chpass(username, instance, argv);
144 #endif
145 #ifdef  YP
146 	if (_yp_check(NULL))
147 		yp_chpass(username);
148 #endif
149 	local_chpass(argv);
150 	/* NOTREACHED */
151 	exit(0);
152 }
153 
154 void
155 local_chpass(argv)
156 	char *argv[];
157 {
158 
159 	/* login_lchpass doesn't check instance so don't bother restoring it */
160 	argv[0] = strrchr(_PATH_LOGIN_LCHPASS, '/') + 1;
161 	execv(_PATH_LOGIN_LCHPASS, argv);
162 	syslog(LOG_ERR, "%s: %m", _PATH_LOGIN_LCHPASS);
163 	exit(1);
164 }
165 
166 #ifdef YP
167 void
168 yp_chpass(username)
169 	char *username;
170 {
171 	FILE *back;
172 	char *master;
173 	int r, rpcport, status;
174 	struct yppasswd yppasswd;
175 	struct passwd *pw;
176 	struct timeval tv;
177 	CLIENT *client;
178 	extern char *domain;
179 
180 	if (!(back = fdopen(3, "a")))  {
181 		syslog(LOG_ERR, "reopening back channel: %m");
182 		exit(1);
183 	}
184 
185 	if ((r = yp_get_default_domain(&domain)) != 0) {
186 		warnx("can't get local YP domain. Reason: %s", yperr_string(r));
187 		exit(1);
188 	}
189 
190 	/*
191 	 * Find the host for the passwd map; it should be running
192 	 * the daemon.
193 	 */
194 	if ((r = yp_master(domain, "passwd.byname", &master)) != 0) {
195 		warnx("can't find the master YP server. Reason: %s",
196 		    yperr_string(r));
197 		exit(1);
198 	}
199 
200 	/* Ask the portmapper for the port of the daemon. */
201 	if ((rpcport = getrpcport(master, YPPASSWDPROG,
202 	    YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) {
203 		warnx("master YP server not running yppasswd daemon.");
204 		warnx("Can't change password.");
205 		exit(1);
206 	}
207 
208 	if (rpcport >= IPPORT_RESERVED) {
209 		warnx("yppasswd daemon is on an invalid port.");
210 		exit(1);
211 	}
212 
213 	/* If user doesn't exist, just prompt for old password and exit. */
214 	if ((pw = ypgetpwnam(username)) == NULL) {
215 		char *p = getpass("Old password:");
216 		crypt(p, "xx");
217 		memset(p, 0, strlen(p));
218 		warnx("YP passwd database unchanged.");
219 		exit(1);
220 	}
221 
222 	if (*pw->pw_passwd == '\0') {
223 		syslog(LOG_ERR, "%s attempting to add password", username);
224 		fprintf(back, BI_SILENT "\n");
225 		exit(0);
226 	}
227 
228 	/* prompt for new password */
229 	yppasswd.newpw.pw_passwd = ypgetnewpasswd(pw, &yppasswd.oldpass);
230 
231 	/* tell rpc.yppasswdd */
232 	yppasswd.newpw.pw_name	= pw->pw_name;
233 	yppasswd.newpw.pw_uid 	= pw->pw_uid;
234 	yppasswd.newpw.pw_gid	= pw->pw_gid;
235 	yppasswd.newpw.pw_gecos = pw->pw_gecos;
236 	yppasswd.newpw.pw_dir	= pw->pw_dir;
237 	yppasswd.newpw.pw_shell	= pw->pw_shell;
238 
239 	client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
240 	if (client == NULL) {
241 		warnx("cannot contact yppasswdd on %s: Reason: %s",
242 		    master, yperr_string(YPERR_YPBIND));
243 		free(yppasswd.newpw.pw_passwd);
244 		exit(1);
245 	}
246 	client->cl_auth = authunix_create_default();
247 	tv.tv_sec = 2;
248 	tv.tv_usec = 0;
249 	r = clnt_call(client, YPPASSWDPROC_UPDATE,
250 	    xdr_yppasswd, &yppasswd, xdr_int, &status, tv);
251 	if (r)
252 		warnx("rpc to yppasswdd failed.");
253 	else if (status) {
254 		printf("Couldn't change YP password.\n");
255 		free(yppasswd.newpw.pw_passwd);
256 		exit(1);
257 	}
258 	printf("The YP password has been changed on %s, the master YP passwd server.\n",
259 	    master);
260 	free(yppasswd.newpw.pw_passwd);
261 	fprintf(back, BI_SILENT "\n");
262 	exit(0);
263 }
264 #endif
265 
266 #ifdef KERBEROS
267 void
268 krb_chpass(username, instance, argv)
269 	char *username;
270 	char *instance;
271 	char *argv[];
272 {
273 	FILE *back;
274 	int rval;
275 	char pword[MAX_KPW_LEN];
276 	char tktstring[MAXPATHLEN];
277 	krb_principal principal;
278 
279 	if (!(back = fdopen(3, "a")))  {
280 		syslog(LOG_ERR, "reopening back channel: %m");
281 		exit(1);
282 	}
283 
284 	memset(&principal, 0, sizeof(principal));
285 	krb_get_default_principal(principal.name,
286 	    principal.instance, principal.realm);
287 
288 	snprintf(tktstring, sizeof(tktstring), "%s.chpass.%s.%d",
289 	    TKT_ROOT, username, getpid());
290 	krb_set_tkt_string(tktstring);
291 
292 	(void)setpriority(PRIO_PROCESS, 0, -4);
293 
294 	if (get_pw_new_pwd(pword, sizeof(pword), &principal, 0)) {
295 		dest_tkt();
296 		exit(1);
297 	}
298 
299 	rval = kadm_init_link (PWSERV_NAME, KRB_MASTER, principal.realm);
300 	if (rval != KADM_SUCCESS)
301 		com_err(argv[0], rval, "while initializing");
302 	else {
303 		des_cblock newkey;
304 		char *pw_msg; /* message from server */
305 
306 		des_string_to_key(pword, &newkey);
307 		rval = kadm_change_pw_plain((u_char *)&newkey, pword, &pw_msg);
308 		memset(newkey, 0, sizeof(newkey));
309 
310 		if (rval == KADM_INSECURE_PW)
311 			warnx("Insecure password: %s", pw_msg);
312 		else if (rval != KADM_SUCCESS)
313 			com_err(argv[0], rval, "attempting to change password.");
314 	}
315 	memset(pword, 0, sizeof(pword));
316 
317 	if (rval != KADM_SUCCESS)
318 		fprintf(stderr, "Password NOT changed.\n");
319 	else
320 		printf("Password changed.\n");
321 
322 	dest_tkt();
323 
324 	if (rval == 0)
325 		fprintf(back, BI_SILENT "\n");
326     	exit(rval);
327 }
328 #endif
329