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