]> granicus.if.org Git - shadow/blob - src/chgpasswd.c
Same changes as for chpasswd:
[shadow] / src / chgpasswd.c
1 /*
2  * Copyright 1990 - 1994, Julianne Frances Haugh
3  * 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. Neither the name of Julianne F. Haugh nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #ident "$Id$"
33
34 #include <fcntl.h>
35 #include <getopt.h>
36 #include <pwd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #ifdef USE_PAM
40 #include "pam_defs.h"
41 #endif                          /* USE_PAM */
42 #include "defines.h"
43 #include "nscd.h"
44 #include "prototypes.h"
45 #include "groupio.h"
46 #ifdef  SHADOWGRP
47 #include "sgroupio.h"
48 #endif
49 /*
50  * Global variables
51  */
52 static char *Prog;
53 static int cflg = 0;
54 static int eflg = 0;
55 static int md5flg = 0;
56 static int sflg = 0;
57
58 static char *crypt_method = NULL;
59 static long sha_rounds = 5000;
60
61 #ifdef SHADOWGRP
62 static int is_shadow_grp;
63 #endif
64
65 /* local function prototypes */
66 static void usage (void);
67 static void process_flags (int argc, char **argv);
68 static void check_flags (void);
69 static void check_perms (void);
70 static void open_files (void);
71 static void close_files (void);
72
73 /*
74  * usage - display usage message and exit
75  */
76 static void usage (void)
77 {
78         fprintf (stderr, _("Usage: %s [options]\n"
79                            "\n"
80                            "Options:\n"
81                            "  -c, --crypt-method            the crypt method (one of %s)\n"
82                            "  -e, --encrypted               supplied passwords are encrypted\n"
83                            "  -h, --help                    display this help message and exit\n"
84                            "  -m, --md5                     encrypt the clear text password using\n"
85                            "                                the MD5 algorithm\n"
86                            "%s"
87                            "\n"),
88                          Prog,
89 #ifndef USE_SHA_CRYPT
90                          "NONE DES MD5", ""
91 #else
92                          "NONE DES MD5 SHA256 SHA512",
93                          _("  -s, --sha-rounds              number of SHA rounds for the SHA*\n"
94                            "                                crypt algorithms\n")
95 #endif
96                          );
97         exit (1);
98 }
99
100 /*
101  * process_flags - parse the command line options
102  *
103  *      It will not return if an error is encountered.
104  */
105 static void process_flags (int argc, char **argv)
106 {
107         int option_index = 0;
108         int c;
109         static struct option long_options[] = {
110                 {"crypt-method", required_argument, NULL, 'c'},
111                 {"encrypted", no_argument, NULL, 'e'},
112                 {"help", no_argument, NULL, 'h'},
113                 {"md5", no_argument, NULL, 'm'},
114 #ifdef USE_SHA_CRYPT
115                 {"sha-rounds", required_argument, NULL, 's'},
116 #endif
117                 {NULL, 0, NULL, '\0'}
118         };
119
120         while ((c = getopt_long (argc, argv,
121 #ifdef USE_SHA_CRYPT
122                                  "c:ehms:",
123 #else
124                                  "c:ehm",
125 #endif
126                                  long_options, &option_index)) != -1) {
127                 switch (c) {
128                 case 'c':
129                         cflg = 1;
130                         crypt_method = optarg;
131                         break;
132                 case 'e':
133                         eflg = 1;
134                         break;
135                 case 'h':
136                         usage ();
137                         break;
138                 case 'm':
139                         md5flg = 1;
140                         break;
141 #ifdef USE_SHA_CRYPT
142                 case 's':
143                         sflg = 1;
144                         if (!getlong(optarg, &sha_rounds)) {
145                                 fprintf (stderr,
146                                          _("%s: invalid numeric argument '%s'\n"),
147                                          Prog, optarg);
148                                 usage ();
149                         }
150                         break;
151 #endif
152                 case 0:
153                         /* long option */
154                         break;
155                 default:
156                         usage ();
157                         break;
158                 }
159         }
160
161         /* validate options */
162         check_flags ();
163 }
164
165 /*
166  * check_flags - check flags and parameters consistency
167  *
168  *      It will not return if an error is encountered.
169  */
170 static void check_flags (void)
171 {
172         if (sflg && !cflg) {
173                 fprintf (stderr,
174                          _("%s: %s flag is ONLY allowed with the %s flag\n"),
175                          Prog, "-s", "-c");
176                 usage ();
177         }
178
179         if ((eflg && (md5flg || cflg)) ||
180             (md5flg && cflg)) {
181                 fprintf (stderr,
182                          _("%s: the -c, -e, and -m flags are exclusive\n"),
183                          Prog);
184                 usage ();
185         }
186
187         if (cflg) {
188                 if (   0 != strcmp (crypt_method, "DES")
189                     && 0 != strcmp (crypt_method, "MD5")
190                     && 0 != strcmp (crypt_method, "NONE")
191 #ifdef USE_SHA_CRYPT
192                     && 0 != strcmp (crypt_method, "SHA256")
193                     && 0 != strcmp (crypt_method, "SHA512")
194 #endif
195                     ) {
196                         fprintf (stderr,
197                                  _("%s: unsupported crypt method: %s\n"),
198                                  Prog, crypt_method);
199                         usage ();
200                 }
201         }
202 }
203
204 /*
205  * check_perms - check if the caller is allowed to add a group
206  *
207  *      With PAM support, the setuid bit can be set on groupadd to allow
208  *      non-root users to groups.
209  *      Without PAM support, only users who can write in the group databases
210  *      can add groups.
211  *
212  *      It will not return if the user is not allowed.
213  */
214 static void check_perms (void)
215 {
216 #ifdef USE_PAM
217         pam_handle_t *pamh = NULL;
218         int retval = PAM_SUCCESS;
219
220         struct passwd *pampw;
221         pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
222         if (pampw == NULL) {
223                 retval = PAM_USER_UNKNOWN;
224         }
225
226         if (retval == PAM_SUCCESS) {
227                 retval = pam_start ("chgpasswd", pampw->pw_name, &conv, &pamh);
228         }
229
230         if (retval == PAM_SUCCESS) {
231                 retval = pam_authenticate (pamh, 0);
232                 if (retval != PAM_SUCCESS) {
233                         pam_end (pamh, retval);
234                 }
235         }
236
237         if (retval == PAM_SUCCESS) {
238                 retval = pam_acct_mgmt (pamh, 0);
239                 if (retval != PAM_SUCCESS) {
240                         pam_end (pamh, retval);
241                 }
242         }
243
244         if (retval != PAM_SUCCESS) {
245                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
246                 exit (1);
247         }
248 #endif                          /* USE_PAM */
249 }
250
251 /*
252  * open_files - lock and open the group databases
253  */
254 static void open_files (void)
255 {
256         /*
257          * Lock the group file and open it for reading and writing. This will
258          * bring all of the entries into memory where they may be updated.
259          */
260         if (!gr_lock ()) {
261                 fprintf (stderr, _("%s: can't lock group file\n"), Prog);
262                 exit (1);
263         }
264         if (!gr_open (O_RDWR)) {
265                 fprintf (stderr, _("%s: can't open group file\n"), Prog);
266                 gr_unlock ();
267                 exit (1);
268         }
269
270 #ifdef SHADOWGRP
271         /* Do the same for the shadowed database, if it exist */
272         if (is_shadow_grp) {
273                 if (!sgr_lock ()) {
274                         fprintf (stderr, _("%s: can't lock gshadow file\n"),
275                                  Prog);
276                         gr_unlock ();
277                         exit (1);
278                 }
279                 if (!sgr_open (O_RDWR)) {
280                         fprintf (stderr, _("%s: can't open shadow file\n"),
281                                  Prog);
282                         gr_unlock ();
283                         sgr_unlock ();
284                         exit (1);
285                 }
286         }
287 #endif
288 }
289
290 /*
291  * close_files - close and unlock the group databases
292  */
293 static void close_files (void)
294 {
295 #ifdef SHADOWGRP
296         if (is_shadow_grp) {
297                 if (!sgr_close ()) {
298                         fprintf (stderr,
299                                  _("%s: error updating shadow file\n"), Prog);
300                         gr_unlock ();
301                         exit (1);
302                 }
303                 sgr_unlock ();
304         }
305 #endif
306
307         if (!gr_close ()) {
308                 fprintf (stderr, _("%s: error updating password file\n"), Prog);
309                 exit (1);
310         }
311         gr_unlock ();
312 }
313
314 int main (int argc, char **argv)
315 {
316         char buf[BUFSIZ];
317         char *name;
318         char *newpwd;
319         char *cp;
320
321 #ifdef  SHADOWGRP
322         const struct sgrp *sg;
323         struct sgrp newsg;
324 #endif
325
326         const struct group *gr;
327         struct group newgr;
328         int errors = 0;
329         int line = 0;
330         int ok;
331
332         Prog = Basename (argv[0]);
333
334         setlocale (LC_ALL, "");
335         bindtextdomain (PACKAGE, LOCALEDIR);
336         textdomain (PACKAGE);
337
338         process_flags(argc, argv);
339
340         check_perms ();
341
342         is_shadow_grp = sgr_file_present ();
343
344         open_files ();
345
346         /*
347          * Read each line, separating the group name from the password. The
348          * password entry for each group will be looked up in the appropriate
349          * file (gshadow or group) and the password changed.
350          */
351         while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
352                 line++;
353                 if ((cp = strrchr (buf, '\n'))) {
354                         *cp = '\0';
355                 } else {
356                         fprintf (stderr, _("%s: line %d: line too long\n"),
357                                  Prog, line);
358                         errors++;
359                         continue;
360                 }
361
362                 /*
363                  * The groupname is the first field. It is separated from the
364                  * password with a ":" character which is replaced with a
365                  * NUL to give the new password. The new password will then
366                  * be encrypted in the normal fashion with a new salt
367                  * generated, unless the '-e' is given, in which case it is
368                  * assumed to already be encrypted.
369                  */
370
371                 name = buf;
372                 if ((cp = strchr (name, ':'))) {
373                         *cp++ = '\0';
374                 } else {
375                         fprintf (stderr,
376                                  _("%s: line %d: missing new password\n"),
377                                  Prog, line);
378                         errors++;
379                         continue;
380                 }
381                 newpwd = cp;
382                 if (!eflg &&
383                     (NULL == crypt_method ||
384                      0 != strcmp(crypt_method, "NONE"))) {
385                         void *arg = NULL;
386                         if (md5flg)
387                                 crypt_method = "MD5";
388                         else if (crypt_method != NULL) {
389                                 if (sflg)
390                                         arg = &sha_rounds;
391                         } else
392                                 crypt_method = NULL;
393                         cp = pw_encrypt (newpwd,
394                                          crypt_make_salt(crypt_method, arg));
395                 }
396
397                 /*
398                  * Get the password file entry for this user. The user must
399                  * already exist.
400                  */
401                 gr = gr_locate (name);
402                 if (!gr) {
403                         fprintf (stderr,
404                                  _("%s: line %d: unknown group %s\n"), Prog,
405                                  line, name);
406                         errors++;
407                         continue;
408                 }
409 #ifdef SHADOWGRP
410                 if (is_shadow_grp)
411                         sg = sgr_locate (name);
412                 else
413                         sg = NULL;
414 #endif
415
416                 /*
417                  * The freshly encrypted new password is merged into the
418                  * user's password file entry and the last password change
419                  * date is set to the current date.
420                  */
421 #ifdef SHADOWGRP
422                 if (sg) {
423                         newsg = *sg;
424                         newsg.sg_passwd = cp;
425                 } else
426 #endif
427                 {
428                         newgr = *gr;
429                         newgr.gr_passwd = cp;
430                 }
431
432                 /* 
433                  * The updated password file entry is then put back and will
434                  * be written to the password file later, after all the
435                  * other entries have been updated as well.
436                  */
437 #ifdef SHADOWGRP
438                 if (sg)
439                         ok = sgr_update (&newsg);
440                 else
441 #endif
442                         ok = gr_update (&newgr);
443
444                 if (!ok) {
445                         fprintf (stderr,
446                                  _
447                                  ("%s: line %d: cannot update password entry\n"),
448                                  Prog, line);
449                         errors++;
450                         continue;
451                 }
452         }
453
454         /*
455          * Any detected errors will cause the entire set of changes to be
456          * aborted. Unlocking the password file will cause all of the
457          * changes to be ignored. Otherwise the file is closed, causing the
458          * changes to be written out all at once, and then unlocked
459          * afterwards.
460          */
461         if (errors) {
462                 fprintf (stderr,
463                          _("%s: error detected, changes ignored\n"), Prog);
464 #ifdef SHADOWGRP
465                 if (is_shadow_grp)
466                         sgr_unlock ();
467 #endif
468                 gr_unlock ();
469                 exit (1);
470         }
471
472         close_files ();
473
474         nscd_flush_cache ("group");
475
476 #ifdef USE_PAM
477         pam_end (pamh, PAM_SUCCESS);
478 #endif                          /* USE_PAM */
479
480         return (0);
481 }