2bf39b6fc3e0e894401cffe2f6dba47d14946b0f
[dragonfly.git] / usr.bin / chpass / chpass.c
1 /*-
2  * Copyright (c) 1988, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2002 Networks Associates Technology, Inc.
5  * All rights reserved.
6  *
7  * Portions of this software were developed for the FreeBSD Project by
8  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10  * ("CBOSS"), as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#) Copyright (c) 1988, 1993, 1994 The Regents of the University of California.  All rights reserved.
37  * @(#)chpass.c 8.4 (Berkeley) 4/2/94
38  * $FreeBSD: src/usr.bin/chpass/chpass.c,v 1.28 2006/09/25 15:06:24 marck Exp $
39  * $DragonFly: src/usr.bin/chpass/chpass.c,v 1.4 2003/11/03 19:31:28 eirikn Exp $
40  */
41
42 #include <sys/param.h>
43
44 #include <err.h>
45 #include <errno.h>
46 #include <pwd.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #ifdef YP
52 #include <ypclnt.h>
53 #endif
54
55 #include <pw_scan.h>
56 #include <libutil.h>
57
58 #include "chpass.h"
59
60 int master_mode;
61
62 static void     baduser(void);
63 static void     usage(void);
64
65 int
66 main(int argc, char **argv)
67 {
68         enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
69         struct passwd lpw, *old_pw, *pw;
70         int ch, pfd, tfd;
71         const char *password;
72         char *cryptpw;
73         char *arg = NULL;
74         uid_t uid;
75 #ifdef YP
76         struct ypclnt *ypclnt;
77         const char *yp_domain = NULL, *yp_host = NULL;
78 #endif
79
80         pw = old_pw = NULL;
81         op = EDITENTRY;
82 #ifdef YP
83         while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1)
84 #else
85         while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
86 #endif
87                 switch (ch) {
88                 case 'a':
89                         op = LOADENTRY;
90                         arg = optarg;
91                         break;
92                 case 's':
93                         op = NEWSH;
94                         arg = optarg;
95                         break;
96                 case 'p':
97                         op = NEWPW;
98                         arg = optarg;
99                         break;
100                 case 'e':
101                         op = NEWEXP;
102                         arg = optarg;
103                         break;
104 #ifdef YP
105                 case 'd':
106                         yp_domain = optarg;
107                         break;
108                 case 'h':
109                         yp_host = optarg;
110                         break;
111                 case 'l':
112                 case 'o':
113                 case 'y':
114                         /* compatibility */
115                         break;
116 #endif
117                 case '?':
118                 default:
119                         usage();
120                 }
121
122         argc -= optind;
123         argv += optind;
124
125         if (argc > 1)
126                 usage();
127
128         uid = getuid();
129
130         if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
131                 if (argc == 0) {
132                         if ((pw = getpwuid(uid)) == NULL)
133                                 errx(1, "unknown user: uid %lu",
134                                     (unsigned long)uid);
135                 } else {
136                         if ((pw = getpwnam(*argv)) == NULL)
137                                 errx(1, "unknown user: %s", *argv);
138                         if (uid != 0 && uid != pw->pw_uid)
139                                 baduser();
140                 }
141
142                 /* Make a copy for later verification */
143                 if ((pw = pw_dup(pw)) == NULL ||
144                     (old_pw = pw_dup(pw)) == NULL)
145                         err(1, "pw_dup");
146         }
147
148 #ifdef YP
149         if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
150                 ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
151                 master_mode = (ypclnt != NULL &&
152                     ypclnt_connect(ypclnt) != -1 &&
153                     ypclnt_havepasswdd(ypclnt) == 1);
154                 ypclnt_free(ypclnt);
155         } else
156 #endif
157         master_mode = (uid == 0);
158
159         if (op == NEWSH) {
160                 /* protect p_shell -- it thinks NULL is /bin/sh */
161                 if (!arg[0])
162                         usage();
163                 if (p_shell(arg, pw, NULL) == -1)
164                         exit(1);
165         }
166
167         if (op == NEWEXP) {
168                 if (uid)        /* only root can change expire */
169                         baduser();
170                 if (p_expire(arg, pw, NULL) == -1)
171                         exit(1);
172         }
173
174         if (op == LOADENTRY) {
175                 if (uid)
176                         baduser();
177                 pw = &lpw;
178                 old_pw = NULL;
179                 if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
180                         exit(1);
181         }
182
183         if (op == NEWPW) {
184                 if (uid)
185                         baduser();
186
187                 if (strchr(arg, ':'))
188                         errx(1, "invalid format for password");
189                 pw->pw_passwd = arg;
190         }
191
192         if (op == EDITENTRY) {
193                 /*
194                  * We don't really need pw_*() here, but pw_edit() (used
195                  * by edit()) is just too useful...
196                  */
197                 if (pw_init(NULL, NULL))
198                         err(1, "pw_init()");
199                 if ((tfd = pw_tmp(-1)) == -1) {
200                         pw_fini();
201                         err(1, "pw_tmp()");
202                 }
203                 free(pw);
204                 pw = edit(pw_tempname(), old_pw);
205                 pw_fini();
206                 if (pw == NULL)
207                         err(1, "edit()");
208                 /*
209                  * pw_equal does not check for crypted passwords, so we
210                  * should do it explicitly
211                  */
212                 if (pw_equal(old_pw, pw) &&
213                     strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0)
214                         errx(0, "user information unchanged");
215         }
216
217         if (old_pw && !master_mode) {
218                 password = getpass("Password: ");
219                 cryptpw = crypt(password, old_pw->pw_passwd);
220                 if (cryptpw == NULL || strcmp(cryptpw, old_pw->pw_passwd) != 0)
221                         baduser();
222         } else {
223                 password = "";
224         }
225
226         if (old_pw != NULL)
227                 pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE);
228         switch (pw->pw_fields & _PWF_SOURCE) {
229 #ifdef YP
230         case _PWF_NIS:
231                 ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
232                 if (ypclnt == NULL ||
233                     ypclnt_connect(ypclnt) == -1 ||
234                     ypclnt_passwd(ypclnt, pw, password) == -1) {
235                         warnx("%s", ypclnt->error);
236                         ypclnt_free(ypclnt);
237                         exit(1);
238                 }
239                 ypclnt_free(ypclnt);
240                 errx(0, "NIS user information updated");
241 #endif /* YP */
242         case 0:
243         case _PWF_FILES:
244                 if (pw_init(NULL, NULL))
245                         err(1, "pw_init()");
246                 if ((pfd = pw_lock()) == -1) {
247                         pw_fini();
248                         err(1, "pw_lock()");
249                 }
250                 if ((tfd = pw_tmp(-1)) == -1) {
251                         pw_fini();
252                         err(1, "pw_tmp()");
253                 }
254                 if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
255                         pw_fini();
256                         err(1, "pw_copy");
257                 }
258                 if (pw_mkdb(pw->pw_name) == -1) {
259                         pw_fini();
260                         err(1, "pw_mkdb()");
261                 }
262                 pw_fini();
263                 errx(0, "user information updated");
264                 break;
265         default:
266                 errx(1, "unsupported passwd source");
267         }
268 }
269
270 static void
271 baduser(void)
272 {
273
274         errx(1, "%s", strerror(EACCES));
275 }
276
277 static void
278 usage(void)
279 {
280
281         fprintf(stderr,
282             "usage: chpass%s %s [user]\n",
283 #ifdef YP
284             " [-d domain] [-h host]",
285 #else
286             "",
287 #endif
288             "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]");
289         exit(1);
290 }