/* * process_flags - parse the command line options * * It will not return if an error is encountered. */ static void process_flags (int argc, char **argv) { int arg; /* * Parse the command line arguments */ while ((arg = getopt (argc, argv, "qrs")) != EOF) { switch (arg) { case 'q': /* quiet - ignored for now */ break; case 'r': read_only = true; break; case 's': sort_mode = true; break; default: usage (); } } if (sort_mode && read_only) { fprintf (stderr, _("%s: -s and -r are incompatibile\n"), Prog); exit (E_USAGE); } /* * Make certain we have the right number of arguments */ #ifdef SHADOWGRP if ((argc < optind) || (argc > (optind + 2))) #else if ((argc < optind) || (argc > (optind + 1))) #endif { usage (); } /* * If there are two left over filenames, use those as the group and * group password filenames. */ if (optind != argc) { grp_file = argv[optind]; gr_setdbname (grp_file); use_system_grp_file = false; } #ifdef SHADOWGRP if ((optind + 2) == argc) { sgr_file = argv[optind + 1]; sgr_setdbname (sgr_file); is_shadow = true; use_system_sgr_file = false; } else if (optind == argc) { is_shadow = sgr_file_present (); } #endif }
/* * main - groupadd command */ int main (int argc, char **argv) { /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); prefix = process_prefix_flag ("-P", argc, argv); OPENLOG ("groupadd"); #ifdef WITH_AUDIT audit_help_open (); #endif if (atexit (do_cleanups) != 0) { fprintf (stderr, _("%s: Cannot setup cleanup service.\n"), Prog); exit (1); } /* * Parse the command line options. */ process_flags (argc, argv); check_perms (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif /* * Do the hard stuff - open the files, create the group entries, * then close and update the files. */ open_files (); if (!gflg) { if (find_new_gid (rflg, &group_id, NULL) < 0) { exit (E_GID_IN_USE); } } grp_update (); close_files (); nscd_flush_cache ("group"); return E_SUCCESS; }
int main (int argc, char **argv) { const struct group *gr; struct group grent; const struct sgrp *sg; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("grpunconv"); process_flags (argc, argv); if (sgr_file_present () == 0) { exit (0); /* no /etc/gshadow, nothing to do */ } if (gr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, gr_dbname ()); fail_exit (5); } gr_locked = true; if (gr_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); fail_exit (1); } if (sgr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, sgr_dbname ()); fail_exit (5); } sgr_locked = true; if (sgr_open (O_RDONLY) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); fail_exit (1); } /* * Update group passwords if non-shadow password is "x". */ (void) gr_rewind (); while ((gr = gr_next ()) != NULL) { sg = sgr_locate (gr->gr_name); if ( (NULL != sg) && (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) == 0)) { /* add password to /etc/group */ grent = *gr; grent.gr_passwd = sg->sg_passwd; if (gr_update (&grent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, gr_dbname (), grent.gr_name); fail_exit (3); } } } (void) sgr_close (); /* was only open O_RDONLY */ if (gr_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); fail_exit (3); } if (unlink (SGROUP_FILE) != 0) { fprintf (stderr, _("%s: cannot delete %s\n"), Prog, SGROUP_FILE); SYSLOG ((LOG_ERR, "cannot delete %s", SGROUP_FILE)); fail_exit (3); } if (gr_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); /* continue */ } if (sgr_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); /* continue */ } nscd_flush_cache ("group"); return 0; }
int main (int argc, char **argv) { #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ #ifdef WITH_AUDIT audit_help_open (); #endif atexit (do_cleanups); /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); if (argc != 2) { usage (); } group_name = argv[1]; OPENLOG ("groupdel"); #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM { struct passwd *pampw; pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */ if (pampw == NULL) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); exit (1); } retval = pam_start ("groupdel", pampw->pw_name, &conv, &pamh); } if (PAM_SUCCESS == retval) { retval = pam_authenticate (pamh, 0); } if (PAM_SUCCESS == retval) { retval = pam_acct_mgmt (pamh, 0); } if (NULL != pamh) { (void) pam_end (pamh, retval); } if (PAM_SUCCESS != retval) { fprintf (stderr, _("%s: PAM authentication failed\n"), Prog); exit (1); } #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif { struct group *grp; /* * Start with a quick check to see if the group exists. */ grp = getgrnam (group_name); /* local, no need for xgetgrnam */ if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), Prog, group_name); exit (E_NOTFOUND); } group_id = grp->gr_gid; } #ifdef USE_NIS /* * Make sure this isn't a NIS group */ if (__isgrNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: group '%s' is a NIS group\n"), Prog, group_name); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "group.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif /* * Make sure this isn't the primary group of anyone. */ group_busy (group_id); /* * Do the hard stuff - open the files, delete the group entries, * then close and update the files. */ open_files (); grp_update (); close_files (); nscd_flush_cache ("group"); return E_SUCCESS; }
int main (int argc, char **argv) { #ifdef USE_PAM pam_handle_t *pamh = NULL; struct passwd *pampw; int retval; #endif /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); #ifdef USE_PAM retval = PAM_SUCCESS; pampw = getpwuid (getuid ()); if (pampw == NULL) { retval = PAM_USER_UNKNOWN; } if (retval == PAM_SUCCESS) { retval = pam_start ("shadow", pampw->pw_name, &conv, &pamh); } if (retval == PAM_SUCCESS) { retval = pam_authenticate (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval == PAM_SUCCESS) { retval = pam_acct_mgmt (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval != PAM_SUCCESS) { fprintf (stderr, _("%s: PAM authentication failed\n"), Prog); exit (1); } #endif /* USE_PAM */ OPENLOG (Prog); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif /* * The open routines for the DBM files don't use read-write as the * mode, so we have to clue them in. */ #ifdef NDBM gr_dbm_mode = O_RDWR; #ifdef SHADOWGRP sg_dbm_mode = O_RDWR; #endif /* SHADOWGRP */ #endif /* NDBM */ process_flags (argc, argv); /* * Start with a quick check to see if the group exists. */ if (getgrnam (group_name)) { if (fflg) { exit (E_SUCCESS); } fprintf (stderr, _("%s: group %s exists\n"), Prog, group_name); exit (E_NAME_IN_USE); } /* * Do the hard stuff - open the files, create the group entries, * then close and update the files. */ open_files (); if (!gflg || !oflg) find_new_gid (); grp_update (); close_files (); #ifdef USE_PAM if (retval == PAM_SUCCESS) { retval = pam_chauthtok (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval != PAM_SUCCESS) { fprintf (stderr, _("%s: PAM chauthtok failed\n"), Prog); exit (1); } if (retval == PAM_SUCCESS) pam_end (pamh, PAM_SUCCESS); #endif /* USE_PAM */ exit (E_SUCCESS); /*NOTREACHED*/ }
/* * gpasswd - administer the /etc/group file */ int main (int argc, char **argv) { struct group grent; #ifdef SHADOWGRP struct sgrp sgent; #endif struct passwd *pw = NULL; #ifdef WITH_AUDIT audit_help_open (); #endif sanitize_env (); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); /* * Make a note of whether or not this command was invoked by root. * This will be used to bypass certain checks later on. Also, set * the real user ID to match the effective user ID. This will * prevent the invoker from issuing signals which would interfere * with this command. */ bywho = getuid (); Prog = Basename (argv[0]); OPENLOG ("gpasswd"); setbuf (stdout, NULL); setbuf (stderr, NULL); #ifdef SHADOWGRP is_shadowgrp = sgr_file_present (); #endif /* * Determine the name of the user that invoked this command. This * is really hit or miss because there are so many ways that command * can be executed and so many ways to trip up the routines that * report the user name. */ pw = get_my_pwent (); if (NULL == pw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", (unsigned long) getuid ())); exit (E_NOPERM); } myname = xstrdup (pw->pw_name); /* * Register an exit function to warn for any inconsistency that we * could create. */ if (atexit (do_cleanups) != 0) { fprintf(stderr, "%s: cannot set exit function\n", Prog); exit (1); } /* Parse the options */ process_flags (argc, argv); /* * Replicate the group so it can be modified later on. */ #ifdef SHADOWGRP get_group (&grent, &sgent); #else get_group (&grent); #endif /* * Check if the user is allowed to change the password of this group. */ #ifdef SHADOWGRP check_perms (&grent, &sgent); #else check_perms (&grent); #endif /* * Removing a password is straight forward. Just set the password * field to a "". */ if (rflg) { grent.gr_passwd = ""; /* XXX warning: const */ #ifdef SHADOWGRP sgent.sg_passwd = ""; /* XXX warning: const */ #endif goto output; } else if (Rflg) { /* * Same thing for restricting the group. Set the password * field to "!". */ grent.gr_passwd = "!"; /* XXX warning: const */ #ifdef SHADOWGRP sgent.sg_passwd = "!"; /* XXX warning: const */ #endif goto output; } /* * Adding a member to a member list is pretty straightforward as * well. Call the appropriate routine and split. */ if (aflg) { printf (_("Adding user %s to group %s\n"), user, group); grent.gr_mem = add_list (grent.gr_mem, user); #ifdef SHADOWGRP if (is_shadowgrp) { sgent.sg_mem = add_list (sgent.sg_mem, user); } #endif goto output; } /* * Removing a member from the member list is the same deal as adding * one, except the routine is different. */ if (dflg) { bool removed = false; printf (_("Removing user %s from group %s\n"), user, group); if (is_on_list (grent.gr_mem, user)) { removed = true; grent.gr_mem = del_list (grent.gr_mem, user); } #ifdef SHADOWGRP if (is_shadowgrp) { if (is_on_list (sgent.sg_mem, user)) { removed = true; sgent.sg_mem = del_list (sgent.sg_mem, user); } } #endif if (!removed) { fprintf (stderr, _("%s: user '%s' is not a member of '%s'\n"), Prog, user, group); exit (E_BAD_ARG); } goto output; } #ifdef SHADOWGRP /* * Replacing the entire list of administrators is simple. Check the * list to make sure everyone is a real user. Then slap the new list * in place. */ if (Aflg) { sgent.sg_adm = comma_to_list (admins); if (!Mflg) { goto output; } } #endif /* SHADOWGRP */ /* * Replacing the entire list of members is simple. Check the list to * make sure everyone is a real user. Then slap the new list in * place. */ if (Mflg) { #ifdef SHADOWGRP sgent.sg_mem = comma_to_list (members); #endif grent.gr_mem = comma_to_list (members); goto output; } /* * If the password is being changed, the input and output must both * be a tty. The typical keyboard signals are caught so the termio * modes can be restored. */ if ((isatty (0) == 0) || (isatty (1) == 0)) { fprintf (stderr, _("%s: Not a tty\n"), Prog); exit (E_NOPERM); } catch_signals (0); /* save tty modes */ (void) signal (SIGHUP, catch_signals); (void) signal (SIGINT, catch_signals); (void) signal (SIGQUIT, catch_signals); (void) signal (SIGTERM, catch_signals); #ifdef SIGTSTP (void) signal (SIGTSTP, catch_signals); #endif /* Prompt for the new password */ #ifdef SHADOWGRP change_passwd (&grent, &sgent); #else change_passwd (&grent); #endif /* * This is the common arrival point to output the new group file. * The freshly crafted entry is in allocated space. The group file * will be locked and opened for writing. The new entry will be * output, etc. */ output: if (setuid (0) != 0) { fputs (_("Cannot change ID to root.\n"), stderr); SYSLOG ((LOG_ERR, "can't setuid(0)")); closelog (); exit (E_NOPERM); } pwd_init (); open_files (); #ifdef SHADOWGRP update_group (&grent, &sgent); #else update_group (&grent); #endif close_files (); nscd_flush_cache ("group"); exit (E_SUCCESS); }
int main (int argc, char **argv) { char buf[BUFSIZ]; char *name; char *newpwd; char *cp; #ifdef SHADOWGRP const struct sgrp *sg; struct sgrp newsg; #endif const struct group *gr; struct group newgr; int errors = 0; int line = 0; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); process_flags (argc, argv); OPENLOG ("chgpasswd"); check_perms (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif open_files (); /* * Read each line, separating the group name from the password. The * group entry for each group will be looked up in the appropriate * file (gshadow or group) and the password changed. */ while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) { line++; cp = strrchr (buf, '\n'); if (NULL != cp) { *cp = '\0'; } else { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; continue; } /* * The group's name is the first field. It is separated from * the password with a ":" character which is replaced with a * NUL to give the new password. The new password will then * be encrypted in the normal fashion with a new salt * generated, unless the '-e' is given, in which case it is * assumed to already be encrypted. */ name = buf; cp = strchr (name, ':'); if (NULL != cp) { *cp = '\0'; cp++; } else { fprintf (stderr, _("%s: line %d: missing new password\n"), Prog, line); errors++; continue; } newpwd = cp; if ( (!eflg) && ( (NULL == crypt_method) || (0 != strcmp (crypt_method, "NONE")))) { void *arg = NULL; const char *salt; if (md5flg) { crypt_method = "MD5"; } #ifdef USE_SHA_CRYPT if (sflg) { arg = &sha_rounds; } #endif salt = crypt_make_salt (crypt_method, arg); cp = pw_encrypt (newpwd, salt); if (NULL == cp) { fprintf (stderr, _("%s: failed to crypt password with salt '%s': %s\n"), Prog, salt, strerror (errno)); fail_exit (1); } } /* * Get the group file entry for this group. The group must * already exist. */ gr = gr_locate (name); if (NULL == gr) { fprintf (stderr, _("%s: line %d: group '%s' does not exist\n"), Prog, line, name); errors++; continue; } #ifdef SHADOWGRP if (is_shadow_grp) { /* The gshadow entry should be updated if the * group entry has a password set to 'x'. * But on the other hand, if there is already both * a group and a gshadow password, it's preferable * to update both. */ sg = sgr_locate (name); if ( (NULL == sg) && (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) == 0)) { static char *empty = NULL; /* If the password is set to 'x' in * group, but there are no entries in * gshadow, create one. */ newsg.sg_name = name; /* newsg.sg_passwd = NULL; will be set later */ newsg.sg_adm = ∅ newsg.sg_mem = dup_list (gr->gr_mem); sg = &newsg; } } else { sg = NULL; } #endif /* * The freshly encrypted new password is merged into the * group's entry. */ #ifdef SHADOWGRP if (NULL != sg) { newsg = *sg; newsg.sg_passwd = cp; } if ( (NULL == sg) || (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) != 0)) #endif { newgr = *gr; newgr.gr_passwd = cp; } /* * The updated group file entry is then put back and will * be written to the group file later, after all the * other entries have been updated as well. */ #ifdef SHADOWGRP if (NULL != sg) { if (sgr_update (&newsg) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, sgr_dbname (), newsg.sg_name); errors++; continue; } } if ( (NULL == sg) || (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) != 0)) #endif { if (gr_update (&newgr) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, gr_dbname (), newgr.gr_name); errors++; continue; } } } /* * Any detected errors will cause the entire set of changes to be * aborted. Unlocking the group file will cause all of the * changes to be ignored. Otherwise the file is closed, causing the * changes to be written out all at once, and then unlocked * afterwards. */ if (0 != errors) { fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog); fail_exit (1); } close_files (); nscd_flush_cache ("group"); return (0); }
int main (int argc, char **argv) { char buf[BUFSIZ]; char *fields[8]; int nfields; char *cp; const struct passwd *pw; struct passwd newpw; int errors = 0; int line = 0; uid_t uid; gid_t gid; #ifdef USE_PAM int *lines = NULL; char **usernames = NULL; char **passwords = NULL; unsigned int nusers = 0; #endif /* USE_PAM */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); /* FIXME: will not work with an input file */ process_root_flag ("-R", argc, argv); OPENLOG ("newusers"); process_flags (argc, argv); check_perms (); is_shadow = spw_file_present (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif #ifdef ENABLE_SUBIDS is_sub_uid = sub_uid_file_present () && !rflg; is_sub_gid = sub_gid_file_present () && !rflg; #endif /* ENABLE_SUBIDS */ open_files (); /* * Read each line. The line has the same format as a password file * entry, except that certain fields are not constrained to be * numerical values. If a group ID is entered which does not already * exist, an attempt is made to allocate the same group ID as the * numerical user ID. Should that fail, the next available group ID * over 100 is allocated. The pw_gid field will be updated with that * value. */ while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) { line++; cp = strrchr (buf, '\n'); if (NULL != cp) { *cp = '\0'; } else { if (feof (stdin) == 0) { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; continue; } } /* * Break the string into fields and screw around with them. * There MUST be 7 colon separated fields, although the * values aren't that particular. */ for (cp = buf, nfields = 0; nfields < 7; nfields++) { fields[nfields] = cp; cp = strchr (cp, ':'); if (NULL != cp) { *cp = '\0'; cp++; } else { break; } } if (nfields != 6) { fprintf (stderr, _("%s: line %d: invalid line\n"), Prog, line); errors++; continue; } /* * First check if we have to create or update an user */ pw = pw_locate (fields[0]); /* local, no need for xgetpwnam */ if ( (NULL == pw) && (getpwnam (fields[0]) != NULL)) { fprintf (stderr, _("%s: cannot update the entry of user %s (not in the passwd database)\n"), Prog, fields[0]); errors++; continue; } if ( (NULL == pw) && (get_user_id (fields[2], &uid) != 0)) { fprintf (stderr, _("%s: line %d: can't create user\n"), Prog, line); errors++; continue; } /* * Processed is the group name. A new group will be * created if the group name is non-numeric and does not * already exist. If the group name is a number (which is not * an existing GID), a group with the same name as the user * will be created, with the given GID. The given or created * group will be the primary group of the user. If * there is no named group to be a member of, the UID will * be figured out and that value will be a candidate for a * new group, if that group ID exists, a whole new group ID * will be made up. */ if ( (NULL == pw) && (add_group (fields[0], fields[3], &gid, uid) != 0)) { fprintf (stderr, _("%s: line %d: can't create group\n"), Prog, line); errors++; continue; } /* * Now we work on the user ID. It has to be specified either * as a numerical value, or left blank. If it is a numerical * value, that value will be used, otherwise the next * available user ID is computed and used. After this there * will at least be a (struct passwd) for the user. */ if ( (NULL == pw) && (add_user (fields[0], uid, gid) != 0)) { fprintf (stderr, _("%s: line %d: can't create user\n"), Prog, line); errors++; continue; } /* * The password, gecos field, directory, and shell fields * all come next. */ pw = pw_locate (fields[0]); if (NULL == pw) { fprintf (stderr, _("%s: line %d: user '%s' does not exist in %s\n"), Prog, line, fields[0], pw_dbname ()); errors++; continue; } newpw = *pw; #ifdef USE_PAM /* keep the list of user/password for later update by PAM */ nusers++; lines = realloc (lines, sizeof (lines[0]) * nusers); usernames = realloc (usernames, sizeof (usernames[0]) * nusers); passwords = realloc (passwords, sizeof (passwords[0]) * nusers); lines[nusers-1] = line; usernames[nusers-1] = strdup (fields[0]); passwords[nusers-1] = strdup (fields[1]); #endif /* USE_PAM */ if (add_passwd (&newpw, fields[1]) != 0) { fprintf (stderr, _("%s: line %d: can't update password\n"), Prog, line); errors++; continue; } if ('\0' != fields[4][0]) { newpw.pw_gecos = fields[4]; } if ('\0' != fields[5][0]) { newpw.pw_dir = fields[5]; } if ('\0' != fields[6][0]) { newpw.pw_shell = fields[6]; } if ( ('\0' != fields[5][0]) && (access (newpw.pw_dir, F_OK) != 0)) { /* FIXME: should check for directory */ mode_t msk = 0777 & ~getdef_num ("UMASK", GETDEF_DEFAULT_UMASK); if (mkdir (newpw.pw_dir, msk) != 0) { fprintf (stderr, _("%s: line %d: mkdir %s failed: %s\n"), Prog, line, newpw.pw_dir, strerror (errno)); } else if (chown (newpw.pw_dir, newpw.pw_uid, newpw.pw_gid) != 0) { fprintf (stderr, _("%s: line %d: chown %s failed: %s\n"), Prog, line, newpw.pw_dir, strerror (errno)); } } /* * Update the password entry with the new changes made. */ if (pw_update (&newpw) == 0) { fprintf (stderr, _("%s: line %d: can't update entry\n"), Prog, line); errors++; continue; } #ifdef ENABLE_SUBIDS /* * Add subordinate uids if the user does not have them. */ if (is_sub_uid && !sub_uid_assigned(fields[0])) { uid_t sub_uid_start = 0; unsigned long sub_uid_count = 0; if (find_new_sub_uids(fields[0], &sub_uid_start, &sub_uid_count) == 0) { if (sub_uid_add(fields[0], sub_uid_start, sub_uid_count) == 0) { fprintf (stderr, _("%s: failed to prepare new %s entry\n"), Prog, sub_uid_dbname ()); } } else { fprintf (stderr, _("%s: can't find subordinate user range\n"), Prog); errors++; } } /* * Add subordinate gids if the user does not have them. */ if (is_sub_gid && !sub_gid_assigned(fields[0])) { gid_t sub_gid_start = 0; unsigned long sub_gid_count = 0; if (find_new_sub_gids(fields[0], &sub_gid_start, &sub_gid_count) == 0) { if (sub_gid_add(fields[0], sub_gid_start, sub_gid_count) == 0) { fprintf (stderr, _("%s: failed to prepare new %s entry\n"), Prog, sub_uid_dbname ()); } } else { fprintf (stderr, _("%s: can't find subordinate group range\n"), Prog); errors++; } } #endif /* ENABLE_SUBIDS */ } /* * Any detected errors will cause the entire set of changes to be * aborted. Unlocking the password file will cause all of the * changes to be ignored. Otherwise the file is closed, causing the * changes to be written out all at once, and then unlocked * afterwards. */ if (0 != errors) { fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog); fail_exit (EXIT_FAILURE); } close_files (); nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); #ifdef USE_PAM unsigned int i; /* Now update the passwords using PAM */ for (i = 0; i < nusers; i++) { if (do_pam_passwd_non_interactive ("newusers", usernames[i], passwords[i]) != 0) { fprintf (stderr, _("%s: (line %d, user %s) password not changed\n"), Prog, lines[i], usernames[i]); errors++; } } #endif /* USE_PAM */ return ((0 == errors) ? EXIT_SUCCESS : EXIT_FAILURE); }
/* * process_flags - parse the command line options * * It will not return if an error is encountered. */ static void process_flags (int argc, char **argv) { int c; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"quiet", no_argument, NULL, 'q'}, {"read-only", no_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, {"sort", no_argument, NULL, 's'}, {NULL, 0, NULL, '\0'} }; /* * Parse the command line arguments */ while ((c = getopt_long (argc, argv, "hqrR:s", long_options, NULL)) != -1) { switch (c) { case 'h': usage (E_SUCCESS); /*@notreached@*/break; case 'q': /* quiet - ignored for now */ break; case 'r': read_only = true; break; case 'R': /* no-op, handled in process_root_flag () */ break; case 's': sort_mode = true; break; default: usage (E_USAGE); } } if (sort_mode && read_only) { fprintf (stderr, _("%s: -s and -r are incompatible\n"), Prog); exit (E_USAGE); } /* * Make certain we have the right number of arguments */ #ifdef SHADOWGRP if (argc > (optind + 2)) #else if (argc > (optind + 1)) #endif { usage (E_USAGE); } /* * If there are two left over filenames, use those as the group and * group password filenames. */ if (optind != argc) { grp_file = argv[optind]; gr_setdbname (grp_file); use_system_grp_file = false; } #ifdef SHADOWGRP if ((optind + 2) == argc) { sgr_file = argv[optind + 1]; sgr_setdbname (sgr_file); is_shadow = true; use_system_sgr_file = false; } else if (optind == argc) { is_shadow = sgr_file_present (); } #endif }
/* * process_flags - perform command line argument setting * * process_flags() interprets the command line arguments and sets the * values that the user will be created with accordingly. The values * are checked for sanity. */ static void process_flags (int argc, char **argv) { const struct group *grp; const struct passwd *pwd; const struct spwd *spwd = NULL; int anyflag = 0; int arg; if (argc == 1 || argv[argc - 1][0] == '-') usage (); if (!(pwd = getpwnam (argv[argc - 1]))) { fprintf (stderr, _("%s: user %s does not exist\n"), Prog, argv[argc - 1]); exit (E_NOTFOUND); } user_name = argv[argc - 1]; user_id = pwd->pw_uid; user_gid = pwd->pw_gid; user_comment = xstrdup (pwd->pw_gecos); user_home = xstrdup (pwd->pw_dir); user_shell = xstrdup (pwd->pw_shell); #ifdef WITH_AUDIT user_newname = user_name; user_newid = user_id; user_newgid = user_gid; user_newcomment = user_comment; user_newhome = user_home; user_newshell = user_shell; #endif #ifdef USE_NIS /* * Now make sure it isn't an NIS user. */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: user %s is a NIS user\n"), Prog, user_name); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif if (is_shadow_pwd && (spwd = getspnam (user_name))) { user_expire = spwd->sp_expire; user_inactive = spwd->sp_inact; #ifdef WITH_AUDIT user_newexpire = user_expire; user_newinactive = user_inactive; #endif } { /* * Parse the command line options. */ int c; static struct option long_options[] = { {"append", required_argument, NULL, 'a'}, {"comment", required_argument, NULL, 'c'}, {"home", required_argument, NULL, 'd'}, {"expiredate", required_argument, NULL, 'e'}, {"inactive", required_argument, NULL, 'f'}, {"gid", required_argument, NULL, 'g'}, {"groups", required_argument, NULL, 'G'}, {"help", no_argument, NULL, 'h'}, {"login", required_argument, NULL, 'l'}, {"lock", no_argument, NULL, 'L'}, {"move-home", no_argument, NULL, 'm'}, {"non-unique", no_argument, NULL, 'o'}, {"password", required_argument, NULL, 'p'}, {"shell", required_argument, NULL, 's'}, {"uid", required_argument, NULL, 'u'}, {"unlock", no_argument, NULL, 'U'}, {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, "ac:d:e:f:g:G:l:Lmop:s:u:U", long_options, NULL)) != -1) { switch (c) { case 'a': aflg++; break; case 'c': if (!VALID (optarg)) { fprintf (stderr, _("%s: invalid field `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } #ifdef WITH_AUDIT user_newcomment = optarg; #else user_comment = optarg; #endif cflg++; break; case 'd': if (!VALID (optarg)) { fprintf (stderr, _("%s: invalid field `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } dflg++; user_newhome = optarg; break; case 'e': if (*optarg) { #ifdef WITH_AUDIT user_newexpire = strtoday (optarg); if (user_newexpire == -1) { #else user_expire = strtoday (optarg); if (user_expire == -1) { #endif fprintf (stderr, _ ("%s: invalid date `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } #ifdef WITH_AUDIT user_newexpire *= DAY / SCALE; #else user_expire *= DAY / SCALE; #endif } else #ifdef WITH_AUDIT user_newexpire = -1; #else user_expire = -1; #endif eflg++; break; case 'f': #ifdef WITH_AUDIT user_newinactive = get_number (optarg); #else user_inactive = get_number (optarg); #endif fflg++; break; case 'g': grp = getgr_nam_gid (optarg); if (!grp) { fprintf (stderr, _("%s: unknown group %s\n"), Prog, optarg); exit (E_NOTFOUND); } user_newgid = grp->gr_gid; gflg++; break; case 'G': if (get_groups (optarg)) exit (E_NOTFOUND); Gflg++; break; case 'l': if (!check_user_name (optarg)) { fprintf (stderr, _("%s: invalid field `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } /* * If the name does not really change, we mustn't * set the flag as this will cause rather serious * problems later! */ if (strcmp (user_name, optarg)) lflg++; user_newname = optarg; break; case 'L': if (Uflg || pflg) usage (); Lflg++; break; case 'm': if (!dflg) usage (); mflg++; break; case 'o': if (!uflg) usage (); oflg++; break; case 'p': if (Lflg || Uflg) usage (); user_pass = optarg; pflg++; break; case 's': if (!VALID (optarg)) { fprintf (stderr, _("%s: invalid field `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } #ifdef WITH_AUDIT user_newshell = optarg; #else user_shell = optarg; #endif sflg++; break; case 'u': user_newid = get_id (optarg); uflg++; break; case 'U': if (Lflg && pflg) usage (); Uflg++; break; default: usage (); } anyflag++; } } if (anyflag == 0) { fprintf (stderr, _("%s: no flags given\n"), Prog); exit (E_USAGE); } if (!is_shadow_pwd && (eflg || fflg)) { fprintf (stderr, _ ("%s: shadow passwords required for -e and -f\n"), Prog); exit (E_USAGE); } if (optind != argc - 1) usage (); if (aflg && (!Gflg)) { fprintf (stderr, _("%s: -a flag is ONLY allowed with the -G flag\n"), Prog); usage (); exit (E_USAGE); } if (dflg && strcmp (user_home, user_newhome) == 0) dflg = mflg = 0; if (uflg && user_id == user_newid) uflg = oflg = 0; if (lflg && getpwnam (user_newname)) { fprintf (stderr, _("%s: user %s exists\n"), Prog, user_newname); exit (E_NAME_IN_USE); } if (uflg && !oflg && getpwuid (user_newid)) { fprintf (stderr, _("%s: uid %lu is not unique\n"), Prog, (unsigned long) user_newid); exit (E_UID_IN_USE); } } /* * close_files - close all of the files that were opened * * close_files() closes all of the files that were opened for this new * user. This causes any modified entries to be written out. */ static void close_files (void) { if (!pw_close ()) { fprintf (stderr, _("%s: cannot rewrite password file\n"), Prog); fail_exit (E_PW_UPDATE); } if (is_shadow_pwd && !spw_close ()) { fprintf (stderr, _("%s: cannot rewrite shadow password file\n"), Prog); fail_exit (E_PW_UPDATE); } if (is_shadow_pwd) spw_unlock (); (void) pw_unlock (); /* * Close the DBM and/or flat files */ endpwent (); endspent (); endgrent (); #ifdef SHADOWGRP endsgent (); #endif } /* * open_files - lock and open the password files * * open_files() opens the two password files. */ static void open_files (void) { if (!pw_lock ()) { fprintf (stderr, _("%s: unable to lock password file\n"), Prog); exit (E_PW_UPDATE); } if (!pw_open (O_RDWR)) { fprintf (stderr, _("%s: unable to open password file\n"), Prog); fail_exit (E_PW_UPDATE); } if (is_shadow_pwd && !spw_lock ()) { fprintf (stderr, _("%s: cannot lock shadow password file\n"), Prog); fail_exit (E_PW_UPDATE); } if (is_shadow_pwd && !spw_open (O_RDWR)) { fprintf (stderr, _("%s: cannot open shadow password file\n"), Prog); fail_exit (E_PW_UPDATE); } } /* * usr_update - create the user entries * * usr_update() creates the password file entries for this user and * will update the group entries if required. */ static void usr_update (void) { struct passwd pwent; const struct passwd *pwd; struct spwd spent; const struct spwd *spwd = NULL; /* * Locate the entry in /etc/passwd, which MUST exist. */ pwd = pw_locate (user_name); if (!pwd) { fprintf (stderr, _("%s: %s not found in /etc/passwd\n"), Prog, user_name); fail_exit (E_NOTFOUND); } pwent = *pwd; new_pwent (&pwent); /* * Locate the entry in /etc/shadow. It doesn't have to exist, and * won't be created if it doesn't. */ if (is_shadow_pwd && (spwd = spw_locate (user_name))) { spent = *spwd; new_spent (&spent); } if (lflg || uflg || gflg || cflg || dflg || sflg || pflg || Lflg || Uflg) { if (!pw_update (&pwent)) { fprintf (stderr, _("%s: error changing password entry\n"), Prog); fail_exit (E_PW_UPDATE); } if (lflg && !pw_remove (user_name)) { fprintf (stderr, _("%s: error removing password entry\n"), Prog); fail_exit (E_PW_UPDATE); } } if (spwd && (lflg || eflg || fflg || pflg || Lflg || Uflg)) { if (!spw_update (&spent)) { fprintf (stderr, _ ("%s: error adding new shadow password entry\n"), Prog); fail_exit (E_PW_UPDATE); } if (lflg && !spw_remove (user_name)) { fprintf (stderr, _ ("%s: error removing shadow password entry\n"), Prog); fail_exit (E_PW_UPDATE); } } } /* * move_home - move the user's home directory * * move_home() moves the user's home directory to a new location. The * files will be copied if the directory cannot simply be renamed. */ static void move_home (void) { struct stat sb; if (mflg && stat (user_home, &sb) == 0) { /* * Don't try to move it if it is not a directory * (but /dev/null for example). --marekm */ if (!S_ISDIR (sb.st_mode)) return; if (access (user_newhome, F_OK) == 0) { fprintf (stderr, _("%s: directory %s exists\n"), Prog, user_newhome); fail_exit (E_HOMEDIR); } else if (rename (user_home, user_newhome)) { if (errno == EXDEV) { if (mkdir (user_newhome, sb.st_mode & 0777)) { fprintf (stderr, _ ("%s: can't create %s\n"), Prog, user_newhome); } if (chown (user_newhome, sb.st_uid, sb.st_gid)) { fprintf (stderr, _("%s: can't chown %s\n"), Prog, user_newhome); rmdir (user_newhome); fail_exit (E_HOMEDIR); } if (copy_tree (user_home, user_newhome, uflg ? user_newid : -1, gflg ? user_newgid : -1) == 0) { if (remove_tree (user_home) != 0 || rmdir (user_home) != 0) fprintf (stderr, _ ("%s: warning: failed to completely remove old home directory %s"), Prog, user_home); #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "moving home directory", user_newname, user_newid, 1); #endif return; } (void) remove_tree (user_newhome); (void) rmdir (user_newhome); } fprintf (stderr, _ ("%s: cannot rename directory %s to %s\n"), Prog, user_home, user_newhome); fail_exit (E_HOMEDIR); } #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "moving home directory", user_newname, user_newid, 1); #endif } if (uflg || gflg) { #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing home directory owner", user_newname, user_newid, 1); #endif chown (dflg ? user_newhome : user_home, uflg ? user_newid : user_id, gflg ? user_newgid : user_gid); } } /* * update_files - update the lastlog and faillog files */ static void update_files (void) { struct lastlog ll; struct faillog fl; int fd; /* * Relocate the "lastlog" entries for the user. The old entry is * left alone in case the UID was shared. It doesn't hurt anything * to just leave it be. */ if ((fd = open (LASTLOG_FILE, O_RDWR)) != -1) { lseek (fd, (off_t) user_id * sizeof ll, SEEK_SET); if (read (fd, (char *) &ll, sizeof ll) == sizeof ll) { lseek (fd, (off_t) user_newid * sizeof ll, SEEK_SET); write (fd, (char *) &ll, sizeof ll); } close (fd); } /* * Relocate the "faillog" entries in the same manner. */ if ((fd = open (FAILLOG_FILE, O_RDWR)) != -1) { lseek (fd, (off_t) user_id * sizeof fl, SEEK_SET); if (read (fd, (char *) &fl, sizeof fl) == sizeof fl) { lseek (fd, (off_t) user_newid * sizeof fl, SEEK_SET); write (fd, (char *) &fl, sizeof fl); } close (fd); } } #ifndef NO_MOVE_MAILBOX /* * This is the new and improved code to carefully chown/rename the user's * mailbox. Maybe I am too paranoid but the mail spool dir sometimes * happens to be mode 1777 (this makes mail user agents work without * being setgid mail, but is NOT recommended; they all should be fixed * to use movemail). --marekm */ static void move_mailbox (void) { const char *maildir; char mailfile[1024], newmailfile[1024]; int fd; struct stat st; maildir = getdef_str ("MAIL_DIR"); #ifdef MAIL_SPOOL_DIR if (!maildir && !getdef_str ("MAIL_FILE")) maildir = MAIL_SPOOL_DIR; #endif if (!maildir) return; /* * O_NONBLOCK is to make sure open won't hang on mandatory locks. * We do fstat/fchown to make sure there are no races (someone * replacing /var/spool/mail/luser with a hard link to /etc/passwd * between stat and chown). --marekm */ snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name); fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0); if (fd < 0) { /* no need for warnings if the mailbox doesn't exist */ if (errno != ENOENT) perror (mailfile); return; } if (fstat (fd, &st) < 0) { perror ("fstat"); close (fd); return; } if (st.st_uid != user_id) { /* better leave it alone */ fprintf (stderr, _("%s: warning: %s not owned by %s\n"), Prog, mailfile, user_name); close (fd); return; } if (uflg) { if (fchown (fd, user_newid, (gid_t) - 1) < 0) { perror (_("failed to change mailbox owner")); } #ifdef WITH_AUDIT else { audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing mail file owner", user_newname, user_newid, 1); } #endif } close (fd); if (lflg) { snprintf (newmailfile, sizeof newmailfile, "%s/%s", maildir, user_newname); if (link (mailfile, newmailfile) || unlink (mailfile)) { perror (_("failed to rename mailbox")); } #ifdef WITH_AUDIT else { audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing mail file name", user_newname, user_newid, 1); } #endif } } #endif /* * main - usermod command */ int main (int argc, char **argv) { int grp_err = 0; #ifdef USE_PAM pam_handle_t *pamh = NULL; struct passwd *pampw; int retval; #endif #ifdef WITH_AUDIT audit_help_open (); #endif /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); sys_ngroups = sysconf (_SC_NGROUPS_MAX); user_groups = malloc ((1 + sys_ngroups) * sizeof (char *)); user_groups[0] = (char *) 0; OPENLOG ("usermod"); is_shadow_pwd = spw_file_present (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif process_flags (argc, argv); #ifdef USE_PAM retval = PAM_SUCCESS; pampw = getpwuid (getuid ()); if (pampw == NULL) { retval = PAM_USER_UNKNOWN; } if (retval == PAM_SUCCESS) { retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh); } if (retval == PAM_SUCCESS) { retval = pam_authenticate (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval == PAM_SUCCESS) { retval = pam_acct_mgmt (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval != PAM_SUCCESS) { fprintf (stderr, _("%s: PAM authentication failed\n"), Prog); exit (1); } #endif /* USE_PAM */ /* * Do the hard stuff - open the files, change the user entries, * change the home directory, then close and update the files. */ open_files (); usr_update (); nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); close_files (); if (Gflg || lflg) grp_err = grp_update (); if (mflg) move_home (); #ifndef NO_MOVE_MAILBOX if (lflg || uflg) move_mailbox (); #endif if (uflg) { update_files (); /* * Change the UID on all of the files owned by `user_id' to * `user_newid' in the user's home directory. */ chown_tree (dflg ? user_newhome : user_home, user_id, user_newid, user_gid, gflg ? user_newgid : user_gid); } if (grp_err) exit (E_GRP_UPDATE); #ifdef USE_PAM if (retval == PAM_SUCCESS) pam_end (pamh, PAM_SUCCESS); #endif /* USE_PAM */ exit (E_SUCCESS); /* NOT REACHED */ }
int main(int argc, char **argv) { struct passwd *pwd; int arg; int errors = 0; /* * Get my name so that I can use it to report errors. */ Prog = Basename(argv[0]); setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH); #ifdef SHADOWPWD is_shadow_pwd = spw_file_present(); #endif #ifdef SHADOWGRP is_shadow_grp = sgr_file_present(); #endif /* * The open routines for the DBM files don't use read-write * as the mode, so we have to clue them in. */ #ifdef NDBM pw_dbm_mode = O_RDWR; #ifdef SHADOWPWD sp_dbm_mode = O_RDWR; #endif gr_dbm_mode = O_RDWR; #ifdef SHADOWGRP sg_dbm_mode = O_RDWR; #endif #endif while ((arg = getopt (argc, argv, "fr")) != EOF) { switch (arg) { case 'f': /* force remove even if not owned by user */ fflg++; break; case 'r': /* remove home dir and mailbox */ rflg++; break; default: usage(); } } if (optind + 1 != argc) usage (); /* * Start with a quick check to see if the user exists. */ user_name = argv[argc - 1]; if (! (pwd = getpwnam (user_name))) { fprintf(stderr, _("%s: user %s does not exist\n"), Prog, user_name); exit(E_NOTFOUND); } #ifdef USE_NIS /* * Now make sure it isn't an NIS user. */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf(stderr, _("%s: user %s is a NIS user\n"), Prog, user_name); if (! yp_get_default_domain (&nis_domain) && ! yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf(stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit(E_NOTFOUND); } #endif user_id = pwd->pw_uid; user_home = xstrdup(pwd->pw_dir); /* * Check to make certain the user isn't logged in. */ user_busy (user_name, user_id); /* * Do the hard stuff - open the files, create the user entries, * create the home directory, then close and update the files. */ open_files (); update_user (); update_groups (); #ifndef NO_REMOVE_MAILBOX if (rflg) remove_mailbox(); #endif if (rflg && !fflg && !is_owner(user_id, user_home)) { fprintf(stderr, _("%s: %s not owned by %s, not removing\n"), Prog, user_home, user_name); rflg = 0; errors++; } /* This may be slow, the above should be good enough. */ #ifdef EXTRA_CHECK_HOME_DIR if (rflg && !fflg) { /* * For safety, refuse to remove the home directory * if it would result in removing some other user's * home directory. Still not perfect so be careful, * but should prevent accidents if someone has /home * or / as home directory... --marekm */ setpwent(); while ((pwd = getpwent())) { if (strcmp(pwd->pw_name, user_name) == 0) continue; if (path_prefix(user_home, pwd->pw_dir)) { fprintf(stderr, _("%s: not removing directory %s (would remove home of user %s)\n"), Prog, user_home, pwd->pw_name); rflg = 0; errors++; break; } } } #endif if (rflg) { if (remove_tree(user_home) || rmdir(user_home)) { fprintf(stderr, _("%s: error removing directory %s\n"), Prog, user_home); errors++; } } /* * Cancel any crontabs or at jobs. Have to do this before we * remove the entry from /etc/passwd. */ user_cancel(user_name); close_files (); exit(errors ? E_HOMEDIR : E_SUCCESS); /*NOTREACHED*/ }
/* * main - groupmod command * */ int main (int argc, char **argv) { #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("groupmod"); #ifdef WITH_AUDIT audit_help_open (); #endif if (atexit (do_cleanups) != 0) { fprintf (stderr, _("%s: Cannot setup cleanup service.\n"), Prog); exit (1); } process_flags (argc, argv); #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM { struct passwd *pampw; pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */ if (NULL == pampw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); exit (1); } retval = pam_start ("groupmod", pampw->pw_name, &conv, &pamh); } if (PAM_SUCCESS == retval) { retval = pam_authenticate (pamh, 0); } if (PAM_SUCCESS == retval) { retval = pam_acct_mgmt (pamh, 0); } if (PAM_SUCCESS != retval) { fprintf (stderr, _("%s: PAM: %s\n"), Prog, pam_strerror (pamh, retval)); SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval))); if (NULL != pamh) { (void) pam_end (pamh, retval); } exit (1); } (void) pam_end (pamh, retval); #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif { struct group *grp; /* * Start with a quick check to see if the group exists. */ grp = getgrnam (group_name); /* local, no need for xgetgrnam */ if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), Prog, group_name); exit (E_NOTFOUND); } else { group_id = grp->gr_gid; } } #ifdef USE_NIS /* * Now make sure it isn't an NIS group. */ if (__isgrNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: group %s is a NIS group\n"), Prog, group_name); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "group.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif if (gflg) { check_new_gid (); } if (nflg) { check_new_name (); } lock_files (); /* * Now if the group is not changed, it's our fault. * Make sure failures will be reported. */ prepare_failure_reports (); /* * Do the hard stuff - open the files, create the group entries, * then close and update the files. */ open_files (); grp_update (); close_files (); nscd_flush_cache ("group"); return E_SUCCESS; }
int main (int argc, char **argv) { bool editshadow = false; char *a; bool do_vipw; (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); progname = ((a = strrchr (*argv, '/')) ? a + 1 : *argv); do_vipw = (strcmp (progname, "vigr") != 0); OPENLOG (do_vipw ? "vipw" : "vigr"); { /* * Parse the command line options. */ int c; static struct option long_options[] = { {"group", no_argument, NULL, 'g'}, {"help", no_argument, NULL, 'h'}, {"passwd", no_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, {"shadow", no_argument, NULL, 's'}, #ifdef WITH_TCB {"user", required_argument, NULL, 'u'}, #endif /* WITH_TCB */ {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, #ifdef WITH_TCB "ghpqsu:", #else /* !WITH_TCB */ "ghpqs", #endif /* !WITH_TCB */ long_options, NULL)) != -1) { switch (c) { case 'g': do_vipw = false; break; case 'h': usage (E_SUCCESS); break; case 'p': do_vipw = true; break; case 'q': quiet = true; break; case 's': editshadow = true; break; #ifdef WITH_TCB case 'u': user = optarg; break; #endif /* WITH_TCB */ default: usage (E_USAGE); } } } if (do_vipw) { if (editshadow) { #ifdef WITH_TCB if (getdef_bool ("USE_TCB") && (NULL != user)) { if (shadowtcb_set_user (user) == SHADOWTCB_FAILURE) { fprintf (stderr, _("%s: failed to find tcb directory for %s\n"), progname, user); return E_SHADOW_NOTFOUND; } tcb_mode = true; } #endif /* WITH_TCB */ vipwedit (spw_dbname (), spw_lock, spw_unlock); printf (MSG_WARN_EDIT_OTHER_FILE, spw_dbname (), pw_dbname (), "vipw"); } else { vipwedit (pw_dbname (), pw_lock, pw_unlock); if (spw_file_present ()) { printf (MSG_WARN_EDIT_OTHER_FILE, pw_dbname (), spw_dbname (), "vipw -s"); } } } else { #ifdef SHADOWGRP if (editshadow) { vipwedit (sgr_dbname (), sgr_lock, sgr_unlock); printf (MSG_WARN_EDIT_OTHER_FILE, sgr_dbname (), gr_dbname (), "vigr"); } else { #endif /* SHADOWGRP */ vipwedit (gr_dbname (), gr_lock, gr_unlock); #ifdef SHADOWGRP if (sgr_file_present ()) { printf (MSG_WARN_EDIT_OTHER_FILE, gr_dbname (), sgr_dbname (), "vigr -s"); } } #endif /* SHADOWGRP */ } nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); return E_SUCCESS; }
/* * main - userdel command */ int main (int argc, char **argv) { int errors = 0; /* Error in the removal of the home directory */ #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("userdel"); #ifdef WITH_AUDIT audit_help_open (); #endif /* WITH_AUDIT */ { /* * Parse the command line options. */ int c; static struct option long_options[] = { {"force", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"remove", no_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, #ifdef WITH_SELINUX {"selinux-user", no_argument, NULL, 'Z'}, #endif /* WITH_SELINUX */ {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, #ifdef WITH_SELINUX "fhrR:Z", #else /* !WITH_SELINUX */ "fhrR:", #endif /* !WITH_SELINUX */ long_options, NULL)) != -1) { switch (c) { case 'f': /* force remove even if not owned by user */ fflg = true; break; case 'h': usage (E_SUCCESS); break; case 'r': /* remove home dir and mailbox */ rflg = true; break; case 'R': /* no-op, handled in process_root_flag () */ break; #ifdef WITH_SELINUX case 'Z': if (is_selinux_enabled () > 0) { Zflg = true; } else { fprintf (stderr, _("%s: -Z requires SELinux enabled kernel\n"), Prog); exit (E_BAD_ARG); } break; #endif /* WITH_SELINUX */ default: usage (E_USAGE); } } } if ((optind + 1) != argc) { usage (E_USAGE); } #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM { struct passwd *pampw; pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */ if (pampw == NULL) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); exit (E_PW_UPDATE); } retval = pam_start ("userdel", pampw->pw_name, &conv, &pamh); } if (PAM_SUCCESS == retval) { retval = pam_authenticate (pamh, 0); } if (PAM_SUCCESS == retval) { retval = pam_acct_mgmt (pamh, 0); } if (PAM_SUCCESS != retval) { fprintf (stderr, _("%s: PAM: %s\n"), Prog, pam_strerror (pamh, retval)); SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval))); if (NULL != pamh) { (void) pam_end (pamh, retval); } exit (E_PW_UPDATE); } (void) pam_end (pamh, retval); #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ is_shadow_pwd = spw_file_present (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif /* SHADOWGRP */ #ifdef ENABLE_SUBIDS is_sub_uid = sub_uid_file_present (); is_sub_gid = sub_gid_file_present (); #endif /* ENABLE_SUBIDS */ /* * Start with a quick check to see if the user exists. */ user_name = argv[argc - 1]; { struct passwd *pwd; pwd = getpwnam (user_name); /* local, no need for xgetpwnam */ if (NULL == pwd) { fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, user_name); #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, "deleting user not found", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ exit (E_NOTFOUND); } user_id = pwd->pw_uid; user_gid = pwd->pw_gid; user_home = xstrdup (pwd->pw_dir); } #ifdef WITH_TCB if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { exit (E_NOTFOUND); } #endif /* WITH_TCB */ #ifdef USE_NIS /* * Now make sure it isn't an NIS user. */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: user %s is a NIS user\n"), Prog, user_name); if ( !yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif /* USE_NIS */ /* * Check to make certain the user isn't logged in. * Note: This is a best effort basis. The user may log in between, * a cron job may be started on her behalf, etc. */ if (user_busy (user_name, user_id) != 0) { if (!fflg) { #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, "deleting user logged in", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ exit (E_USER_BUSY); } } /* * Do the hard stuff - open the files, create the user entries, * create the home directory, then close and update the files. */ open_files (); update_user (); update_groups (); if (rflg) { errors += remove_mailbox (); } if (rflg) { int home_owned = is_owner (user_id, user_home); if (-1 == home_owned) { fprintf (stderr, _("%s: %s home directory (%s) not found\n"), Prog, user_name, user_home); rflg = 0; } else if ((0 == home_owned) && !fflg) { fprintf (stderr, _("%s: %s not owned by %s, not removing\n"), Prog, user_home, user_name); rflg = 0; errors++; /* continue */ } } #ifdef EXTRA_CHECK_HOME_DIR /* This may be slow, the above should be good enough. */ if (rflg && !fflg) { struct passwd *pwd; /* * For safety, refuse to remove the home directory if it * would result in removing some other user's home * directory. Still not perfect so be careful, but should * prevent accidents if someone has /home or / as home * directory... --marekm */ setpwent (); while ((pwd = getpwent ())) { if (strcmp (pwd->pw_name, user_name) == 0) { continue; } if (path_prefix (user_home, pwd->pw_dir)) { fprintf (stderr, _("%s: not removing directory %s (would remove home of user %s)\n"), Prog, user_home, pwd->pw_name); rflg = false; errors++; /* continue */ break; } } endpwent (); } #endif /* EXTRA_CHECK_HOME_DIR */ if (rflg) { if (remove_tree (user_home, true) != 0) { fprintf (stderr, _("%s: error removing directory %s\n"), Prog, user_home); errors++; /* continue */ } #ifdef WITH_AUDIT else { audit_logger (AUDIT_DEL_USER, Prog, "deleting home directory", user_name, (unsigned int) user_id, SHADOW_AUDIT_SUCCESS); } #endif /* WITH_AUDIT */ } #ifdef WITH_AUDIT if (0 != errors) { audit_logger (AUDIT_DEL_USER, Prog, "deleting home directory", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); } #endif /* WITH_AUDIT */ #ifdef WITH_SELINUX if (Zflg) { if (del_seuser (user_name) != 0) { fprintf (stderr, _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"), Prog, user_name); #ifdef WITH_AUDIT audit_logger (AUDIT_ADD_USER, Prog, "removing SELinux user mapping", user_name, (unsigned int) user_id, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ fail_exit (E_SE_UPDATE); } } #endif /* WITH_SELINUX */ /* * Cancel any crontabs or at jobs. Have to do this before we remove * the entry from /etc/passwd. */ user_cancel (user_name); close_files (); #ifdef WITH_TCB errors += remove_tcbdir (user_name, user_id); #endif /* WITH_TCB */ nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); return ((0 != errors) ? E_HOMEDIR : E_SUCCESS); }
/* * grpck - verify group file integrity */ int main (int argc, char **argv) { int arg; int errors = 0; int deleted = 0; int i; struct commonio_entry *gre, *tgre; struct group *grp; int sort_mode = 0; #ifdef SHADOWGRP struct commonio_entry *sge, *tsge; struct sgrp *sgr; int is_shadow = 0; #endif /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); OPENLOG ("grpck"); /* * Parse the command line arguments */ while ((arg = getopt (argc, argv, "qrs")) != EOF) { switch (arg) { case 'q': /* quiet - ignored for now */ break; case 'r': read_only = 1; break; case 's': sort_mode = 1; break; default: usage (); } } if (sort_mode && read_only) { fprintf (stderr, _("%s: -s and -r are incompatibile\n"), Prog); exit (E_USAGE); } /* * Make certain we have the right number of arguments */ #ifdef SHADOWGRP if (optind != argc && optind + 1 != argc && optind + 2 != argc) #else if (optind != argc && optind + 1 != argc) #endif usage (); /* * If there are two left over filenames, use those as the group and * group password filenames. */ if (optind != argc) { grp_file = argv[optind]; gr_name (grp_file); } #ifdef SHADOWGRP if (optind + 2 == argc) { sgr_file = argv[optind + 1]; sgr_name (sgr_file); is_shadow = 1; } else if (optind == argc) is_shadow = sgr_file_present (); #endif /* * Lock the files if we aren't in "read-only" mode */ if (!read_only) { if (!gr_lock ()) { fprintf (stderr, _("%s: cannot lock file %s\n"), Prog, grp_file); if (optind == argc) SYSLOG ((LOG_WARN, "cannot lock %s", grp_file)); closelog (); exit (E_CANT_LOCK); } #ifdef SHADOWGRP if (is_shadow && !sgr_lock ()) { fprintf (stderr, _("%s: cannot lock file %s\n"), Prog, sgr_file); if (optind == argc) SYSLOG ((LOG_WARN, "cannot lock %s", sgr_file)); closelog (); exit (E_CANT_LOCK); } #endif } /* * Open the files. Use O_RDONLY if we are in read_only mode, * O_RDWR otherwise. */ if (!gr_open (read_only ? O_RDONLY : O_RDWR)) { fprintf (stderr, _("%s: cannot open file %s\n"), Prog, grp_file); if (optind == argc) SYSLOG ((LOG_WARN, "cannot open %s", grp_file)); closelog (); exit (E_CANT_OPEN); } #ifdef SHADOWGRP if (is_shadow && !sgr_open (read_only ? O_RDONLY : O_RDWR)) { fprintf (stderr, _("%s: cannot open file %s\n"), Prog, sgr_file); if (optind == argc) SYSLOG ((LOG_WARN, "cannot open %s", sgr_file)); closelog (); exit (E_CANT_OPEN); } #endif if (sort_mode) { gr_sort (); #ifdef SHADOWGRP if (is_shadow) sgr_sort (); #endif goto write_and_bye; } /* * Loop through the entire group file. */ for (gre = __gr_get_head (); gre; gre = gre->next) { /* * Skip all NIS entries. */ if (gre->line[0] == '+' || gre->line[0] == '-') continue; /* * Start with the entries that are completely corrupt. They * have no (struct group) entry because they couldn't be * parsed properly. */ if (!gre->eptr) { /* * Tell the user this entire line is bogus and ask * them to delete it. */ printf (_("invalid group file entry\n")); printf (_("delete line `%s'? "), gre->line); errors++; /* * prompt the user to delete the entry or not */ if (!yes_or_no ()) continue; /* * All group file deletions wind up here. This code * removes the current entry from the linked list. * When done, it skips back to the top of the loop * to try out the next list element. */ delete_gr: SYSLOG ((LOG_INFO, "delete group line `%s'", gre->line)); deleted++; __gr_del_entry (gre); continue; } /* * Group structure is good, start using it. */ grp = gre->eptr; /* * Make sure this entry has a unique name. */ for (tgre = __gr_get_head (); tgre; tgre = tgre->next) { const struct group *ent = tgre->eptr; /* * Don't check this entry */ if (tgre == gre) continue; /* * Don't check invalid entries. */ if (!ent) continue; if (strcmp (grp->gr_name, ent->gr_name) != 0) continue; /* * Tell the user this entry is a duplicate of * another and ask them to delete it. */ printf (_("duplicate group entry\n")); printf (_("delete line `%s'? "), gre->line); errors++; /* * prompt the user to delete the entry or not */ if (yes_or_no ()) goto delete_gr; } /* * Check for invalid group names. --marekm */ if (!check_group_name (grp->gr_name)) { errors++; printf (_("invalid group name `%s'\n"), grp->gr_name); } /* * Workaround for a NYS libc 5.3.12 bug on RedHat 4.2 - * groups with no members are returned as groups with one * member "", causing grpck to fail. --marekm */ if (grp->gr_mem[0] && !grp->gr_mem[1] && *(grp->gr_mem[0]) == '\0') grp->gr_mem[0] = (char *) 0; /* * Make sure each member exists */ for (i = 0; grp->gr_mem[i]; i++) { if (getpwnam (grp->gr_mem[i])) continue; /* * Can't find this user. Remove them * from the list. */ errors++; printf (_("group %s: no user %s\n"), grp->gr_name, grp->gr_mem[i]); printf (_("delete member `%s'? "), grp->gr_mem[i]); if (!yes_or_no ()) continue; SYSLOG ((LOG_INFO, "delete member `%s' group `%s'", grp->gr_mem[i], grp->gr_name)); deleted++; delete_member (grp->gr_mem, grp->gr_mem[i]); gre->changed = 1; __gr_set_changed (); } } #ifdef SHADOWGRP if (!is_shadow) goto shadow_done; /* * Loop through the entire shadow group file. */ for (sge = __sgr_get_head (); sge; sge = sge->next) { /* * Start with the entries that are completely corrupt. They * have no (struct sgrp) entry because they couldn't be * parsed properly. */ if (!sge->eptr) { /* * Tell the user this entire line is bogus and ask * them to delete it. */ printf (_("invalid shadow group file entry\n")); printf (_("delete line `%s'? "), sge->line); errors++; /* * prompt the user to delete the entry or not */ if (!yes_or_no ()) continue; /* * All shadow group file deletions wind up here. * This code removes the current entry from the * linked list. When done, it skips back to the top * of the loop to try out the next list element. */ delete_sg: SYSLOG ((LOG_INFO, "delete shadow line `%s'", sge->line)); deleted++; __sgr_del_entry (sge); continue; } /* * Shadow group structure is good, start using it. */ sgr = sge->eptr; /* * Make sure this entry has a unique name. */ for (tsge = __sgr_get_head (); tsge; tsge = tsge->next) { const struct sgrp *ent = tsge->eptr; /* * Don't check this entry */ if (tsge == sge) continue; /* * Don't check invalid entries. */ if (!ent) continue; if (strcmp (sgr->sg_name, ent->sg_name) != 0) continue; /* * Tell the user this entry is a duplicate of * another and ask them to delete it. */ printf (_("duplicate shadow group entry\n")); printf (_("delete line `%s'? "), sge->line); errors++; /* * prompt the user to delete the entry or not */ if (yes_or_no ()) goto delete_sg; } /* * Make sure this entry exists in the /etc/group file. */ if (!gr_locate (sgr->sg_name)) { printf (_("no matching group file entry\n")); printf (_("delete line `%s'? "), sge->line); errors++; if (yes_or_no ()) goto delete_sg; } /* * Make sure each administrator exists */ for (i = 0; sgr->sg_adm[i]; i++) { if (getpwnam (sgr->sg_adm[i])) continue; /* * Can't find this user. Remove them * from the list. */ errors++; printf (_ ("shadow group %s: no administrative user %s\n"), sgr->sg_name, sgr->sg_adm[i]); printf (_("delete administrative member `%s'? "), sgr->sg_adm[i]); if (!yes_or_no ()) continue; SYSLOG ((LOG_INFO, "delete admin `%s' from shadow group `%s'", sgr->sg_adm[i], sgr->sg_name)); deleted++; delete_member (sgr->sg_adm, sgr->sg_adm[i]); sge->changed = 1; __sgr_set_changed (); } /* * Make sure each member exists */ for (i = 0; sgr->sg_mem[i]; i++) { if (getpwnam (sgr->sg_mem[i])) continue; /* * Can't find this user. Remove them from the list. */ errors++; printf (_("shadow group %s: no user %s\n"), sgr->sg_name, sgr->sg_mem[i]); printf (_("delete member `%s'? "), sgr->sg_mem[i]); if (!yes_or_no ()) continue; SYSLOG ((LOG_INFO, "delete member `%s' from shadow group `%s'", sgr->sg_mem[i], sgr->sg_name)); deleted++; delete_member (sgr->sg_mem, sgr->sg_mem[i]); sge->changed = 1; __sgr_set_changed (); } } shadow_done: #endif /* SHADOWGRP */ /* * All done. If there were no deletions we can just abandon any * changes to the files. */ if (deleted) { write_and_bye: if (!gr_close ()) { fprintf (stderr, _("%s: cannot update file %s\n"), Prog, grp_file); exit (E_CANT_UPDATE); } #ifdef SHADOWGRP if (is_shadow && !sgr_close ()) { fprintf (stderr, _("%s: cannot update file %s\n"), Prog, sgr_file); exit (E_CANT_UPDATE); } #endif } /* * Don't be anti-social - unlock the files when you're done. */ #ifdef SHADOWGRP if (is_shadow) sgr_unlock (); #endif (void) gr_unlock (); nscd_flush_cache ("group"); /* * Tell the user what we did and exit. */ if (errors) printf (deleted ? _("%s: the files have been updated\n") : _("%s: no changes\n"), Prog); exit (errors ? E_BAD_ENTRY : E_OKAY); }