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 long sha_rounds = 5000;
62 static int is_shadow_grp;
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);
74 * usage - display usage message and exit
76 static void usage (void)
78 fprintf (stderr, _("Usage: %s [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"
92 "NONE DES MD5 SHA256 SHA512",
93 _(" -s, --sha-rounds number of SHA rounds for the SHA*\n"
94 " crypt algorithms\n")
101 * process_flags - parse the command line options
103 * It will not return if an error is encountered.
105 static void process_flags (int argc, char **argv)
107 int option_index = 0;
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'},
115 {"sha-rounds", required_argument, NULL, 's'},
117 {NULL, 0, NULL, '\0'}
120 while ((c = getopt_long (argc, argv,
126 long_options, &option_index)) != -1) {
130 crypt_method = optarg;
144 if (!getlong(optarg, &sha_rounds)) {
146 _("%s: invalid numeric argument '%s'\n"),
161 /* validate options */
166 * check_flags - check flags and parameters consistency
168 * It will not return if an error is encountered.
170 static void check_flags (void)
174 _("%s: %s flag is ONLY allowed with the %s flag\n"),
179 if ((eflg && (md5flg || cflg)) ||
182 _("%s: the -c, -e, and -m flags are exclusive\n"),
188 if ( 0 != strcmp (crypt_method, "DES")
189 && 0 != strcmp (crypt_method, "MD5")
190 && 0 != strcmp (crypt_method, "NONE")
192 && 0 != strcmp (crypt_method, "SHA256")
193 && 0 != strcmp (crypt_method, "SHA512")
197 _("%s: unsupported crypt method: %s\n"),
205 * check_perms - check if the caller is allowed to add a group
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
212 * It will not return if the user is not allowed.
214 static void check_perms (void)
217 pam_handle_t *pamh = NULL;
218 int retval = PAM_SUCCESS;
220 struct passwd *pampw;
221 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
223 retval = PAM_USER_UNKNOWN;
226 if (retval == PAM_SUCCESS) {
227 retval = pam_start ("chgpasswd", pampw->pw_name, &conv, &pamh);
230 if (retval == PAM_SUCCESS) {
231 retval = pam_authenticate (pamh, 0);
232 if (retval != PAM_SUCCESS) {
233 pam_end (pamh, retval);
237 if (retval == PAM_SUCCESS) {
238 retval = pam_acct_mgmt (pamh, 0);
239 if (retval != PAM_SUCCESS) {
240 pam_end (pamh, retval);
244 if (retval != PAM_SUCCESS) {
245 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
252 * open_files - lock and open the group databases
254 static void open_files (void)
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.
261 fprintf (stderr, _("%s: can't lock group file\n"), Prog);
264 if (!gr_open (O_RDWR)) {
265 fprintf (stderr, _("%s: can't open group file\n"), Prog);
271 /* Do the same for the shadowed database, if it exist */
274 fprintf (stderr, _("%s: can't lock gshadow file\n"),
279 if (!sgr_open (O_RDWR)) {
280 fprintf (stderr, _("%s: can't open shadow file\n"),
291 * close_files - close and unlock the group databases
293 static void close_files (void)
299 _("%s: error updating shadow file\n"), Prog);
308 fprintf (stderr, _("%s: error updating password file\n"), Prog);
314 int main (int argc, char **argv)
322 const struct sgrp *sg;
326 const struct group *gr;
332 Prog = Basename (argv[0]);
334 setlocale (LC_ALL, "");
335 bindtextdomain (PACKAGE, LOCALEDIR);
336 textdomain (PACKAGE);
338 process_flags(argc, argv);
342 is_shadow_grp = sgr_file_present ();
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.
351 while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
353 if ((cp = strrchr (buf, '\n'))) {
356 fprintf (stderr, _("%s: line %d: line too long\n"),
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.
372 if ((cp = strchr (name, ':'))) {
376 _("%s: line %d: missing new password\n"),
383 (NULL == crypt_method ||
384 0 != strcmp(crypt_method, "NONE"))) {
387 crypt_method = "MD5";
388 else if (crypt_method != NULL) {
393 cp = pw_encrypt (newpwd,
394 crypt_make_salt(crypt_method, arg));
398 * Get the password file entry for this user. The user must
401 gr = gr_locate (name);
404 _("%s: line %d: unknown group %s\n"), Prog,
411 sg = sgr_locate (name);
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.
424 newsg.sg_passwd = cp;
429 newgr.gr_passwd = cp;
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.
439 ok = sgr_update (&newsg);
442 ok = gr_update (&newgr);
447 ("%s: line %d: cannot update password entry\n"),
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
463 _("%s: error detected, changes ignored\n"), Prog);
474 nscd_flush_cache ("group");
477 pam_end (pamh, PAM_SUCCESS);