yp_passwd.c revision 1.18 1 /* $NetBSD: yp_passwd.c,v 1.18 1998/07/12 15:18:55 mrg 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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "from: @(#)local_passwd.c 8.3 (Berkeley) 4/2/94";
40 #else
41 __RCSID("$NetBSD: yp_passwd.c,v 1.18 1998/07/12 15:18:55 mrg Exp $");
42 #endif
43 #endif /* not lint */
44
45 #ifdef YP
46
47 #include <ctype.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <netdb.h>
51 #include <pwd.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <time.h>
56 #include <unistd.h>
57
58 #include <rpc/rpc.h>
59 #include <rpcsvc/yp_prot.h>
60 #include <rpcsvc/ypclnt.h>
61
62 #include "extern.h"
63
64 #define passwd yp_passwd_rec
65 #include <rpcsvc/yppasswd.h>
66 #undef passwd
67
68 #ifndef _PASSWORD_LEN
69 #define _PASSWORD_LEN PASS_MAX
70 #endif
71
72 extern char *__progname; /* from crt0.o */
73
74 extern int yflag, yppwd;
75
76 static char *getnewpasswd __P((struct passwd *, char **));
77 static struct passwd *interpret __P((struct passwd *, char *));
78 static struct passwd *ypgetpwnam __P((char *));
79 static void pw_error __P((char *, int, int));
80 static void test_local __P((char *));
81
82 static uid_t uid;
83 char *domain;
84
85 static void
86 pw_error(name, err, eval)
87 char *name;
88 int err, eval;
89 {
90
91 if (err)
92 warn("%s", name);
93 errx(eval, "YP passwd database unchanged");
94 }
95
96 static void
97 test_local(username)
98 char *username;
99 {
100
101 /*
102 * Something failed recoverably stating that the YP system couldn't
103 * find this user. Look for a local passwd entry, and change that
104 * if and only if we weren't run as yppasswd or with the -y option.
105 * This function does not return if a local entry is found.
106 */
107 if (yppwd == 0 && yflag == 0)
108 if ((getpwnam(username) != NULL) && !local_passwd(username))
109 exit(0);
110 }
111
112 int
113 yp_passwd(username)
114 char *username;
115 {
116 char *master;
117 int r, rpcport, status;
118 struct yppasswd yppasswd;
119 struct passwd *pw;
120 struct timeval tv;
121 CLIENT *client;
122
123 uid = getuid();
124
125 /*
126 * Get local domain
127 */
128 if ((r = yp_get_default_domain(&domain)) != NULL)
129 errx(1, "can't get local YP domain. Reason: %s",
130 yperr_string(r));
131
132 /*
133 * Find the host for the passwd map; it should be running
134 * the daemon.
135 */
136 if ((r = yp_master(domain, "passwd.byname", &master)) != 0) {
137 test_local(username);
138 errx(1, "can't find the master YP server. Reason: %s",
139 yperr_string(r));
140 }
141
142 /*
143 * Ask the portmapper for the port of the daemon.
144 */
145 if ((rpcport = getrpcport(master, YPPASSWDPROG,
146 YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) {
147 test_local(username);
148 errx(1, "master YP server not running yppasswd daemon.\n\t%s\n",
149 "Can't change password.");
150 }
151
152 /*
153 * Be sure the port is priviledged
154 */
155 if (rpcport >= IPPORT_RESERVED)
156 errx(1, "yppasswd daemon is on an invalid port.");
157
158 /* Get user's login identity */
159 if (!(pw = ypgetpwnam(username))) {
160 test_local(username);
161 errx(1, "unknown user %s", username);
162 }
163
164 if (uid && uid != pw->pw_uid)
165 errx(1, "you may only change your own password: %s",
166 strerror(EACCES));
167
168 /* prompt for new password */
169 yppasswd.newpw.pw_passwd = getnewpasswd(pw, &yppasswd.oldpass);
170
171 /* tell rpc.yppasswdd */
172 yppasswd.newpw.pw_name = pw->pw_name;
173 yppasswd.newpw.pw_uid = pw->pw_uid;
174 yppasswd.newpw.pw_gid = pw->pw_gid;
175 yppasswd.newpw.pw_gecos = pw->pw_gecos;
176 yppasswd.newpw.pw_dir = pw->pw_dir;
177 yppasswd.newpw.pw_shell = pw->pw_shell;
178
179 client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
180 if (client == NULL) {
181 warnx("cannot contact yppasswdd on %s: Reason: %s",
182 master, yperr_string(YPERR_YPBIND));
183 return (YPERR_YPBIND);
184 }
185
186 client->cl_auth = authunix_create_default();
187 tv.tv_sec = 2;
188 tv.tv_usec = 0;
189 r = clnt_call(client, YPPASSWDPROC_UPDATE,
190 xdr_yppasswd, &yppasswd, xdr_int, &status, tv);
191 if (r)
192 errx(1, "rpc to yppasswdd failed.");
193 else if (status)
194 printf("Couldn't change YP password.\n");
195 else
196 printf("The YP password has been changed on %s, %s\n",
197 master, "the master YP passwd server.");
198 exit(0);
199 }
200
201 static char *
202 getnewpasswd(pw, old_pass)
203 struct passwd *pw;
204 char **old_pass;
205 {
206 int tries;
207 char *p, *t;
208 static char buf[_PASSWORD_LEN+1];
209 char salt[9];
210
211 (void)printf("Changing YP password for %s.\n", pw->pw_name);
212
213 if (old_pass) {
214 *old_pass = NULL;
215
216 if (pw->pw_passwd[0]) {
217 if (strcmp(crypt(p = getpass("Old password:"),
218 pw->pw_passwd), pw->pw_passwd)) {
219 (void)printf("Sorry.\n");
220 pw_error(NULL, 0, 1);
221 }
222 } else {
223 p = "";
224 }
225
226 *old_pass = strdup(p);
227 }
228 for (buf[0] = '\0', tries = 0;;) {
229 p = getpass("New password:");
230 if (!*p) {
231 (void)printf("Password unchanged.\n");
232 pw_error(NULL, 0, 0);
233 }
234 if (strlen(p) <= 5 && ++tries < 2) {
235 (void)printf("Please enter a longer password.\n");
236 continue;
237 }
238 for (t = p; *t && islower(*t); ++t);
239 if (!*t && ++tries < 2) {
240 (void)printf("Please don't use an all-lower case "
241 "password.\nUnusual capitalization, "
242 "control characters or digits are "
243 "suggested.\n");
244 continue;
245 }
246 (void)strncpy(buf, p, sizeof(buf) - 1);
247 if (!strcmp(buf, getpass("Retype new password:")))
248 break;
249 (void)printf("Mismatch; try again, EOF to quit.\n");
250 }
251 /* grab a random printable character that isn't a colon */
252 (void)srandom((int)time((time_t *)NULL));
253 #ifdef NEWSALT
254 salt[0] = _PASSWORD_EFMT1;
255 to64(&salt[1], (long)(29 * 25), 4);
256 to64(&salt[5], random(), 4);
257 #else
258 to64(&salt[0], random(), 2);
259 #endif
260 return(strdup(crypt(buf, salt)));
261 }
262
263 static char *pwskip __P((char *));
264
265 static char *
266 pwskip(p)
267 char *p;
268 {
269
270 while (*p && *p != ':' && *p != '\n')
271 ++p;
272 if (*p)
273 *p++ = 0;
274 return (p);
275 }
276
277 static struct passwd *
278 interpret(pwent, line)
279 struct passwd *pwent;
280 char *line;
281 {
282 char *p = line;
283
284 pwent->pw_passwd = "*";
285 pwent->pw_uid = 0;
286 pwent->pw_gid = 0;
287 pwent->pw_gecos = "";
288 pwent->pw_dir = "";
289 pwent->pw_shell = "";
290 pwent->pw_change = 0;
291 pwent->pw_expire = 0;
292 pwent->pw_class = "";
293
294 /* line without colon separators is no good, so ignore it */
295 if (!strchr(p,':'))
296 return (NULL);
297
298 pwent->pw_name = p;
299 p = pwskip(p);
300 pwent->pw_passwd = p;
301 p = pwskip(p);
302 pwent->pw_uid = (uid_t)strtoul(p, NULL, 10);
303 p = pwskip(p);
304 pwent->pw_gid = (gid_t)strtoul(p, NULL, 10);
305 p = pwskip(p);
306 pwent->pw_gecos = p;
307 p = pwskip(p);
308 pwent->pw_dir = p;
309 p = pwskip(p);
310 pwent->pw_shell = p;
311 while (*p && *p != '\n')
312 p++;
313 *p = '\0';
314 return (pwent);
315 }
316
317 static struct passwd *
318 ypgetpwnam(nam)
319 char *nam;
320 {
321 static struct passwd pwent;
322 static char line[1024];
323 char *val;
324 int reason, vallen;
325
326 val = NULL;
327 reason = yp_match(domain, "passwd.byname", nam, strlen(nam),
328 &val, &vallen);
329 if (reason != 0) {
330 if (val != NULL)
331 free(val);
332 return (NULL);
333 }
334 val[vallen] = '\0';
335 (void)strncpy(line, val, sizeof(line) - 1);
336 line[sizeof(line) - 1] = '\0';
337 free(val);
338
339 return (interpret(&pwent, line));
340 }
341
342 #endif /* YP */
343