Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1988, 1993, 1994 The Regents of the University of California.  All rights reserved.
34  * @(#)chpass.c 8.4 (Berkeley) 4/2/94
35  * $FreeBSD: src/usr.bin/chpass/chpass.c,v 1.16.2.4 2002/08/11 14:16:42 dwmalone Exp $
36  * $DragonFly: src/usr.bin/chpass/chpass.c,v 1.2 2003/06/17 04:29:25 dillon Exp $
37  */
38
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/signal.h>
42 #include <sys/time.h>
43 #include <sys/resource.h>
44
45 #include <ctype.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <pwd.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54
55 #include <pw_scan.h>
56 #include <pw_util.h>
57 #include "pw_copy.h"
58 #ifdef YP
59 #include <rpcsvc/yp.h>
60 int yp_errno = YP_TRUE;
61 #include "pw_yp.h"
62 #endif
63
64 #include "chpass.h"
65 #include "pathnames.h"
66
67 char *tempname;
68 uid_t uid;
69
70 void    baduser __P((void));
71 void    usage __P((void));
72
73 int
74 main(argc, argv)
75         int argc;
76         char **argv;
77 {
78         enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
79         struct passwd *pw = NULL, lpw, old_pw, *pold_pw;
80         char *username = NULL;
81         int ch, pfd, tfd;
82         char *arg = NULL;
83 #ifdef YP
84         int force_local = 0;
85         int force_yp = 0;
86 #endif
87
88         op = EDITENTRY;
89 #ifdef YP
90         while ((ch = getopt(argc, argv, "a:p:s:e:d:h:oly")) != -1)
91 #else
92         while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
93 #endif
94                 switch(ch) {
95                 case 'a':
96                         op = LOADENTRY;
97                         arg = optarg;
98                         break;
99                 case 's':
100                         op = NEWSH;
101                         arg = optarg;
102                         break;
103                 case 'p':
104                         op = NEWPW;
105                         arg = optarg;
106                         break;
107                 case 'e':
108                         op = NEWEXP;
109                         arg = optarg;
110                         break;
111 #ifdef YP
112                 case 'h':
113 #ifdef PARANOID
114                         if (getuid()) {
115                                 warnx("Only the superuser can use the -h flag");
116                         } else {
117 #endif
118                                 yp_server = optarg;
119 #ifdef PARANOID
120                         }
121 #endif
122                         break;
123                 case 'd':
124 #ifdef PARANOID
125                         if (getuid()) {
126                                 warnx("Only the superuser can use the -d flag");
127                         } else {
128 #endif
129                                 yp_domain = optarg;
130                                 if (yp_server == NULL)
131                                         yp_server = "localhost";
132 #ifdef PARANOID
133                         }
134 #endif
135                         break;
136                 case 'l':
137                         _use_yp = 0;
138                         force_local = 1;
139                         break;
140                 case 'y':
141                         _use_yp = force_yp = 1;
142                         break;
143                 case 'o':
144                         force_old++;
145                         break;
146 #endif
147                 case '?':
148                 default:
149                         usage();
150                 }
151         argc -= optind;
152         argv += optind;
153
154         uid = getuid();
155
156         if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
157                 switch(argc) {
158 #ifdef YP
159                 case 0:
160                         GETPWUID(uid)
161                         get_yp_master(1); /* XXX just to set the suser flag */
162                         break;
163                 case 1:
164                         GETPWNAM(*argv)
165                         get_yp_master(1); /* XXX just to set the suser flag */
166 #else
167                 case 0:
168                         if (!(pw = getpwuid(uid)))
169                                 errx(1, "unknown user: uid %lu",
170                                     (unsigned long)uid);
171                         break;
172                 case 1:
173                         if (!(pw = getpwnam(*argv)))
174                                 errx(1, "unknown user: %s", *argv);
175 #endif
176                         if (uid && uid != pw->pw_uid)
177                                 baduser();
178                         break;
179                 default:
180                         usage();
181                 }
182
183                 /* Make a copy for later verification */
184                 old_pw = *pw;
185                 old_pw.pw_gecos = strdup(old_pw.pw_gecos);
186                 pold_pw = &old_pw;
187         }
188
189         if (op == NEWSH) {
190                 /* protect p_shell -- it thinks NULL is /bin/sh */
191                 if (!arg[0])
192                         usage();
193                 if (p_shell(arg, pw, (ENTRY *)NULL))
194                         pw_error((char *)NULL, 0, 1);
195         }
196
197         if (op == NEWEXP) {
198                 if (uid)        /* only root can change expire */
199                         baduser();
200                 if (p_expire(arg, pw, (ENTRY *)NULL))
201                         pw_error((char *)NULL, 0, 1);
202         }
203
204         if (op == LOADENTRY) {
205                 if (uid)
206                         baduser();
207                 pw = &lpw;
208                 if (!pw_scan(arg, pw))
209                         exit(1);
210                 if ((pold_pw = getpwnam(pw->pw_name)) != NULL) {
211                         old_pw = *pold_pw;
212                         old_pw.pw_gecos = strdup(old_pw.pw_gecos);
213                 }
214         }
215         username = pw->pw_name;
216
217         if (op == NEWPW) {
218                 if (uid)
219                         baduser();
220
221                 if(strchr(arg, ':')) {
222                         errx(1, "invalid format for password");
223                 }
224                 pw->pw_passwd = arg;
225         }
226
227         /*
228          * The temporary file/file descriptor usage is a little tricky here.
229          * 1:   We start off with two fd's, one for the master password
230          *      file (used to lock everything), and one for a temporary file.
231          * 2:   Display() gets an fp for the temporary file, and copies the
232          *      user's information into it.  It then gives the temporary file
233          *      to the user and closes the fp, closing the underlying fd.
234          * 3:   The user edits the temporary file some number of times.
235          * 4:   Verify() gets an fp for the temporary file, and verifies the
236          *      contents.  It can't use an fp derived from the step #2 fd,
237          *      because the user's editor may have created a new instance of
238          *      the file.  Once the file is verified, its contents are stored
239          *      in a password structure.  The verify routine closes the fp,
240          *      closing the underlying fd.
241          * 5:   Delete the temporary file.
242          * 6:   Get a new temporary file/fd.  Pw_copy() gets an fp for it
243          *      file and copies the master password file into it, replacing
244          *      the user record with a new one.  We can't use the first
245          *      temporary file for this because it was owned by the user.
246          *      Pw_copy() closes its fp, flushing the data and closing the
247          *      underlying file descriptor.  We can't close the master
248          *      password fp, or we'd lose the lock.
249          * 7:   Call pw_mkdb() (which renames the temporary file) and exit.
250          *      The exit closes the master passwd fp/fd.
251          */
252         pw_init();
253         tfd = pw_tmp();
254
255         if (op == EDITENTRY) {
256                 display(tfd, pw);
257                 edit(pw);
258                 (void)unlink(tempname);
259                 tfd = pw_tmp();
260         }
261
262 #ifdef YP
263         if (_use_yp) {
264                 yp_submit(pw);
265                 (void)unlink(tempname);
266         } else {
267 #endif /* YP */
268         pfd = pw_lock();
269         pw_copy(pfd, tfd, pw, pold_pw);
270
271         if (!pw_mkdb(username))
272                 pw_error((char *)NULL, 0, 1);
273 #ifdef YP
274         }
275 #endif /* YP */
276         exit(0);
277 }
278
279 void
280 baduser()
281 {
282         errx(1, "%s", strerror(EACCES));
283 }
284
285 void
286 usage()
287 {
288
289         (void)fprintf(stderr,
290 #ifdef YP
291                 "usage: chpass [-o] [-l] [-y] [-d domain] [-h host] [-a list] [-p encpass] [-s shell] [-e mmm dd yy] [user]\n");
292 #else
293                 "usage: chpass [-a list] [-p encpass] [-s shell] [-e mmm dd yy] [user]\n");
294 #endif
295         exit(1);
296 }