2 * Copyright 1990 - 1994, Julianne Frances Haugh
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
44 #include "prototypes.h"
55 static int md5flg = 0;
58 static char *crypt_method = NULL;
59 static int sha_rounds = 5000;
62 static int is_shadow_grp;
65 /* local function prototypes */
66 static void usage (void);
69 * usage - display usage message and exit
71 static void usage (void)
73 fprintf (stderr, _("Usage: chgpasswd [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"
82 #ifndef ENCRYPTMETHOD_SELECT
85 "DES MD5 SHA256 SHA512"
91 static long getnumber (const char *numstr)
96 val = strtol (numstr, &errptr, 10);
97 if (*errptr || errno == ERANGE) {
98 fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog,
106 int main (int argc, char **argv)
114 const struct sgrp *sg;
118 const struct group *gr;
124 pam_handle_t *pamh = NULL;
131 Prog = Basename (argv[0]);
133 setlocale (LC_ALL, "");
134 bindtextdomain (PACKAGE, LOCALEDIR);
135 textdomain (PACKAGE);
138 int option_index = 0;
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'}
150 getopt_long (argc, argv, "c:ehms:", long_options,
151 &option_index)) != -1) {
155 crypt_method = optarg;
168 sha_rounds = getnumber(optarg);
180 /* validate options */
183 _("%s: %s flag is ONLY allowed with the %s flag\n"),
187 if (md5flg && cflg) {
189 _("%s: the -m and -c flags are exclusive\n"),
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")
202 _("%s: unsupported crypt method: %s\n"),
209 retval = PAM_SUCCESS;
212 struct passwd *pampw;
213 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
215 retval = PAM_USER_UNKNOWN;
218 if (retval == PAM_SUCCESS) {
219 retval = pam_start ("chpasswd", pampw->pw_name,
224 if (retval == PAM_SUCCESS) {
225 retval = pam_authenticate (pamh, 0);
226 if (retval != PAM_SUCCESS) {
227 pam_end (pamh, retval);
231 if (retval == PAM_SUCCESS) {
232 retval = pam_acct_mgmt (pamh, 0);
233 if (retval != PAM_SUCCESS) {
234 pam_end (pamh, retval);
238 if (retval != PAM_SUCCESS) {
239 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
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.
249 fprintf (stderr, _("%s: can't lock group file\n"), Prog);
252 if (!gr_open (O_RDWR)) {
253 fprintf (stderr, _("%s: can't open group file\n"), Prog);
258 is_shadow_grp = sgr_file_present ();
261 fprintf (stderr, _("%s: can't lock gshadow file\n"),
266 if (!sgr_open (O_RDWR)) {
267 fprintf (stderr, _("%s: can't open shadow file\n"),
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.
281 while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
283 if ((cp = strrchr (buf, '\n'))) {
286 fprintf (stderr, _("%s: line %d: line too long\n"),
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.
302 if ((cp = strchr (name, ':'))) {
306 _("%s: line %d: missing new password\n"),
315 crypt_method = "MD5";
316 else if (crypt_method != NULL) {
320 crypt_method = "DES";
321 cp = pw_encrypt (newpwd,
322 crypt_make_salt(crypt_method, arg));
326 * Get the password file entry for this user. The user must
329 gr = gr_locate (name);
332 _("%s: line %d: unknown group %s\n"), Prog,
338 sg = sgr_locate (name);
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.
348 newsg.sg_passwd = cp;
351 newgr.gr_passwd = cp;
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.
360 ok = sgr_update (&newsg);
362 ok = gr_update (&newgr);
368 ("%s: line %d: cannot update password entry\n"),
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
384 _("%s: error detected, changes ignored\n"), Prog);
396 _("%s: error updating shadow file\n"), Prog);
404 fprintf (stderr, _("%s: error updating password file\n"), Prog);
408 nscd_flush_cache ("group");
413 if (retval == PAM_SUCCESS)
414 pam_end (pamh, PAM_SUCCESS);