]> granicus.if.org Git - shadow/blob - src/chgpasswd.c
Fix typo s/method/crypt_method/
[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 int 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
68 /*
69  * usage - display usage message and exit
70  */
71 static void usage (void)
72 {
73         fprintf (stderr, _("Usage: chgpasswd [options]\n"
74                            "\n"
75                            "Options:\n"
76                            "  -c, --crypt-method        the crypt method (one of %s)\n"
77                            "  -e, --encrypted   supplied passwords are encrypted\n"
78                            "  -h, --help                display this help message and exit\n"
79                            "  -m, --md5         use MD5 encryption instead DES when the supplied\n"
80                            "                    passwords are not encrypted\n"
81                            "\n"),
82 #ifndef ENCRYPTMETHOD_SELECT
83                          "DES MD5"
84 #else
85                          "DES MD5 SHA256 SHA512"
86 #endif
87                          );
88         exit (1);
89 }
90
91 static long getnumber (const char *numstr)
92 {
93         long val;
94         char *errptr;
95
96         val = strtol (numstr, &errptr, 10);
97         if (*errptr || errno == ERANGE) {
98                 fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog,
99                          numstr);
100                 exit (1);
101         }
102
103         return val;
104 }
105
106 int main (int argc, char **argv)
107 {
108         char buf[BUFSIZ];
109         char *name;
110         char *newpwd;
111         char *cp;
112
113 #ifdef  SHADOWGRP
114         const struct sgrp *sg;
115         struct sgrp newsg;
116 #endif
117
118         const struct group *gr;
119         int errors = 0;
120         int line = 0;
121         int ok;
122
123 #ifdef USE_PAM
124         pam_handle_t *pamh = NULL;
125         int retval;
126 #endif
127 #ifndef SHADOWGRP
128         struct group newgr;
129 #endif
130
131         Prog = Basename (argv[0]);
132
133         setlocale (LC_ALL, "");
134         bindtextdomain (PACKAGE, LOCALEDIR);
135         textdomain (PACKAGE);
136
137         {
138                 int option_index = 0;
139                 int c;
140                 static struct option long_options[] = {
141                         {"crypt-method", required_argument, NULL, 'c'},
142                         {"encrypted", no_argument, NULL, 'e'},
143                         {"help", no_argument, NULL, 'h'},
144                         {"md5", no_argument, NULL, 'm'},
145                         {"sha-rounds", required_argument, NULL, 's'},
146                         {NULL, 0, NULL, '\0'}
147                 };
148
149                 while ((c =
150                         getopt_long (argc, argv, "c:ehms:", long_options,
151                                      &option_index)) != -1) {
152                         switch (c) {
153                         case 'c':
154                                 cflg = 1;
155                                 crypt_method = optarg;
156                                 break;
157                         case 'e':
158                                 eflg = 1;
159                                 break;
160                         case 'h':
161                                 usage ();
162                                 break;
163                         case 'm':
164                                 md5flg = 1;
165                                 break;
166                         case 's':
167                                 sflg = 1;
168                                 sha_rounds = getnumber(optarg);
169                                 break;
170                         case 0:
171                                 /* long option */
172                                 break;
173                         default:
174                                 usage ();
175                                 break;
176                         }
177                 }
178         }
179
180         /* validate options */
181         if (sflg && !cflg) {
182                 fprintf (stderr,
183                          _("%s: %s flag is ONLY allowed with the %s flag\n"),
184                          Prog, "-s", "-c");
185                 usage ();
186         }
187         if (md5flg && cflg) {
188                 fprintf (stderr,
189                          _("%s: the -m and -c flags are exclusive\n"),
190                          Prog);
191                 usage ();
192         }
193         if (cflg) {
194                 if (0 != strcmp (crypt_method, "DES") &&
195                     0 != strcmp (crypt_method, "MD5") &&
196 #ifdef ENCRYPTMETHOD_SELECT
197                     0 != strcmp (crypt_method, "SHA256") &&
198                     0 != strcmp (crypt_method, "SHA512")
199 #endif
200                     ) {
201                         fprintf (stderr,
202                          _("%s: unsupported crypt method: %s\n"),
203                          Prog, crypt_method);
204                         usage ();
205                 }
206         }
207
208 #ifdef USE_PAM
209         retval = PAM_SUCCESS;
210
211         {
212                 struct passwd *pampw;
213                 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
214                 if (pampw == NULL) {
215                         retval = PAM_USER_UNKNOWN;
216                 }
217
218                 if (retval == PAM_SUCCESS) {
219                         retval = pam_start ("chpasswd", pampw->pw_name,
220                                             &conv, &pamh);
221                 }
222         }
223
224         if (retval == PAM_SUCCESS) {
225                 retval = pam_authenticate (pamh, 0);
226                 if (retval != PAM_SUCCESS) {
227                         pam_end (pamh, retval);
228                 }
229         }
230
231         if (retval == PAM_SUCCESS) {
232                 retval = pam_acct_mgmt (pamh, 0);
233                 if (retval != PAM_SUCCESS) {
234                         pam_end (pamh, retval);
235                 }
236         }
237
238         if (retval != PAM_SUCCESS) {
239                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
240                 exit (1);
241         }
242 #endif                          /* USE_PAM */
243
244         /*
245          * Lock the group file and open it for reading. This will bring
246          * all of the entries into memory where they may be updated.
247          */
248         if (!gr_lock ()) {
249                 fprintf (stderr, _("%s: can't lock group file\n"), Prog);
250                 exit (1);
251         }
252         if (!gr_open (O_RDWR)) {
253                 fprintf (stderr, _("%s: can't open group file\n"), Prog);
254                 gr_unlock ();
255                 exit (1);
256         }
257 #ifdef SHADOWGRP
258         is_shadow_grp = sgr_file_present ();
259         if (is_shadow_grp) {
260                 if (!sgr_lock ()) {
261                         fprintf (stderr, _("%s: can't lock gshadow file\n"),
262                                  Prog);
263                         gr_unlock ();
264                         exit (1);
265                 }
266                 if (!sgr_open (O_RDWR)) {
267                         fprintf (stderr, _("%s: can't open shadow file\n"),
268                                  Prog);
269                         gr_unlock ();
270                         sgr_unlock ();
271                         exit (1);
272                 }
273         }
274 #endif
275
276         /*
277          * Read each line, separating the group name from the password. The
278          * password entry for each group will be looked up in the appropriate
279          * file (gshadow or group) and the password changed.
280          */
281         while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
282                 line++;
283                 if ((cp = strrchr (buf, '\n'))) {
284                         *cp = '\0';
285                 } else {
286                         fprintf (stderr, _("%s: line %d: line too long\n"),
287                                  Prog, line);
288                         errors++;
289                         continue;
290                 }
291
292                 /*
293                  * The groupname is the first field. It is separated from the
294                  * password with a ":" character which is replaced with a
295                  * NUL to give the new password. The new password will then
296                  * be encrypted in the normal fashion with a new salt
297                  * generated, unless the '-e' is given, in which case it is
298                  * assumed to already be encrypted.
299                  */
300
301                 name = buf;
302                 if ((cp = strchr (name, ':'))) {
303                         *cp++ = '\0';
304                 } else {
305                         fprintf (stderr,
306                                  _("%s: line %d: missing new password\n"),
307                                  Prog, line);
308                         errors++;
309                         continue;
310                 }
311                 newpwd = cp;
312                 if (!eflg) {
313                         void *arg = NULL;
314                         if (md5flg)
315                                 crypt_method = "MD5";
316                         else if (crypt_method != NULL) {
317                                 if (sflg)
318                                         arg = &sha_rounds;
319                         } else
320                                 crypt_method = "DES";
321                         cp = pw_encrypt (newpwd,
322                                          crypt_make_salt(crypt_method, arg));
323                 }
324
325                 /*
326                  * Get the password file entry for this user. The user must
327                  * already exist.
328                  */
329                 gr = gr_locate (name);
330                 if (!gr) {
331                         fprintf (stderr,
332                                  _("%s: line %d: unknown group %s\n"), Prog,
333                                  line, name);
334                         errors++;
335                         continue;
336                 }
337 #ifdef SHADOWGRP
338                 sg = sgr_locate (name);
339 #endif
340
341                 /*
342                  * The freshly encrypted new password is merged into the
343                  * user's password file entry and the last password change
344                  * date is set to the current date.
345                  */
346 #ifdef SHADOWGRP
347                 newsg = *sg;
348                 newsg.sg_passwd = cp;
349 #else
350                 newgr = *gr;
351                 newgr.gr_passwd = cp;
352 #endif
353
354                 /* 
355                  * The updated password file entry is then put back and will
356                  * be written to the password file later, after all the
357                  * other entries have been updated as well.
358                  */
359 #ifdef SHADOWGRP
360                 ok = sgr_update (&newsg);
361 #else
362                 ok = gr_update (&newgr);
363 #endif
364
365                 if (!ok) {
366                         fprintf (stderr,
367                                  _
368                                  ("%s: line %d: cannot update password entry\n"),
369                                  Prog, line);
370                         errors++;
371                         continue;
372                 }
373         }
374
375         /*
376          * Any detected errors will cause the entire set of changes to be
377          * aborted. Unlocking the password file will cause all of the
378          * changes to be ignored. Otherwise the file is closed, causing the
379          * changes to be written out all at once, and then unlocked
380          * afterwards.
381          */
382         if (errors) {
383                 fprintf (stderr,
384                          _("%s: error detected, changes ignored\n"), Prog);
385 #ifdef SHADOWGRP
386                 if (is_shadow_grp)
387                         sgr_unlock ();
388 #endif
389                 gr_unlock ();
390                 exit (1);
391         }
392 #ifdef SHADOWGRP
393         if (is_shadow_grp) {
394                 if (!sgr_close ()) {
395                         fprintf (stderr,
396                                  _("%s: error updating shadow file\n"), Prog);
397                         gr_unlock ();
398                         exit (1);
399                 }
400                 sgr_unlock ();
401         }
402 #endif
403         if (!gr_close ()) {
404                 fprintf (stderr, _("%s: error updating password file\n"), Prog);
405                 exit (1);
406         }
407
408         nscd_flush_cache ("group");
409
410         gr_unlock ();
411
412 #ifdef USE_PAM
413         if (retval == PAM_SUCCESS)
414                 pam_end (pamh, PAM_SUCCESS);
415 #endif                          /* USE_PAM */
416
417         return (0);
418 }