/* * 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, "eqrs")) != EOF) { switch (arg) { case 'e': /* added for Debian shadow-961025-2 compatibility */ case 'q': quiet = true; 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 incompatible\n"), Prog); exit (E_USAGE); } /* * Make certain we have the right number of arguments */ if ((argc < optind) || (argc > (optind + 2))) { usage (); } /* * If there are two left over filenames, use those as the password * and shadow password filenames. */ if (optind != argc) { pw_setdbname (argv[optind]); use_system_pw_file = false; } if ((optind + 2) == argc) { #ifdef WITH_TCB if (getdef_bool ("USE_TCB")) { fprintf (stderr, _("%s: no alternative shadow file allowed when USE_TCB is enabled.\n"), Prog); usage (); } #endif /* WITH_TCB */ spw_setdbname (argv[optind + 1]); is_shadow = true; use_system_spw_file = false; } else if (optind == argc) { is_shadow = spw_file_present (); } }
int mkdirp(char *dirp, uid_t uid, gid_t gid) { if (mkdir(dirp, 0755) == -1) { if (errno != EEXIST) { fprintf(stderr, "Cannot create directory '%s':%s\n", dirp, strerror(errno)); return -1; } if (getdef_bool("DUPDIROK")) { fprintf(stderr, "\n*** WARNING ***\n\n%s already exists. If it is being used by another user as a home\n" "directory problems could result.\n\n", dirp); fprintf(stderr, "Ownership of %s will be changed to match the new user's.\n\n", dirp); if (!getyn("Use directory %s anyway? (y/N) ", dirp)) return -1; } else { fprintf(stderr, "Directory %s already exists!\n", dirp); fprintf(stderr, "You are not permitted to share directories amongst users\n"); return -1; } } if (chown(dirp, uid, gid) == -1) { fprintf(stderr, "cannot change access to home directory\n"); rmdir(dirp); return -1; } return 0; }
int enter_uid() { long uid; long low, hi; struct passwd *pw; if (getdef_range("UIDRANGE", &low, &hi) < 2) { low = 0; hi = UINT_MAX; } while (1) { uid = guess_uid(); if (inputval_range("User id #", low, hi, uid, &uid) == -1) return -1; if ((pw = getpwuid(uid)) != NULL) { if (getdef_bool("DUPUIDOK")) { fprintf(stderr, "warning, userid %ld is already in use\n", uid); } else { fprintf(stderr, "error, userid %ld is already in use\n", uid); fprintf(stderr, "you are not permitted to share userids\n"); } continue; } return uid; } }
shadowtcb_status shadowtcb_set_user (const char* name) { char *buf; shadowtcb_status retval; if (!getdef_bool ("USE_TCB")) { return SHADOWTCB_SUCCESS; } if (NULL != stored_tcb_user) { free (stored_tcb_user); } stored_tcb_user = strdup (name); if (NULL == stored_tcb_user) { OUT_OF_MEMORY; return SHADOWTCB_FAILURE; } if (asprintf (&buf, TCB_FMT, name) == -1) { OUT_OF_MEMORY; return SHADOWTCB_FAILURE; } retval = (spw_setdbname (buf) != 0) ? SHADOWTCB_SUCCESS : SHADOWTCB_FAILURE; free (buf); return retval; }
/* Returns user's tcb directory path relative to TCB_DIR. */ static /*@null@*/ char *shadowtcb_path_rel (const char *name, uid_t uid) { char *ret; if (!getdef_bool ("TCB_SYMLINKS") || uid < SHADOWTCB_HASH_BY) { if (asprintf (&ret, "%s", name) == -1) { OUT_OF_MEMORY; return NULL; } } else if (uid < SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY) { if (asprintf (&ret, ":%dK/%s", uid / SHADOWTCB_HASH_BY, name) == -1) { OUT_OF_MEMORY; return NULL; } } else { if (asprintf (&ret, ":%dM/:%dK/%s", uid / (SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY), (uid % (SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY)) / SHADOWTCB_HASH_BY, name) == -1) { OUT_OF_MEMORY; return NULL; } } return ret; }
shadowtcb_status shadowtcb_gain_priv (void) { if (!getdef_bool ("USE_TCB")) { return SHADOWTCB_SUCCESS; } return (tcb_gain_priv () == 0) ? SHADOWTCB_SUCCESS : SHADOWTCB_FAILURE; }
/* Sort with respect to passwd ordering. */ int spw_sort () { #ifdef WITH_TCB if (getdef_bool ("USE_TCB")) { return 0; } #endif /* WITH_TCB */ return commonio_sort_wrt (&shadow_db, __pw_get_db ()); }
/* * open_files - open the shadow database * * In read-only mode, the databases are not locked and are opened * only for reading. */ static void open_files (void) { bool use_tcb = false; #ifdef WITH_TCB use_tcb = getdef_bool ("USE_TCB"); #endif /* WITH_TCB */ /* * Lock the files if we aren't in "read-only" mode */ if (!read_only) { if (pw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, pw_dbname ()); fail_exit (E_CANTLOCK); } pw_locked = true; if (is_shadow && !use_tcb) { if (spw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, spw_dbname ()); fail_exit (E_CANTLOCK); } spw_locked = true; } } /* * Open the files. Use O_RDONLY if we are in read_only mode, O_RDWR * otherwise. */ if (pw_open (read_only ? O_RDONLY : O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); if (use_system_pw_file) { SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); } fail_exit (E_CANTOPEN); } pw_opened = true; if (is_shadow && !use_tcb) { if (spw_open (read_only ? O_RDONLY : O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); if (use_system_spw_file) { SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ())); } fail_exit (E_CANTOPEN); } spw_opened = true; } }
/* * change to the user's home directory * set the HOME, SHELL, MAIL, PATH, and LOGNAME or USER environmental * variables. */ void setup_env(struct passwd *info) { char *cp; /* * Change the current working directory to be the home directory * of the user. It is a fatal error for this process to be unable * to change to that directory. There is no "default" home * directory. * * We no longer do it as root - should work better on NFS-mounted * home directories. */ if (chdir(info->pw_dir) == -1) { static char temp_pw_dir[] = "/"; if (!getdef_bool("DEFAULT_HOME") || chdir("/") == -1) { fprintf(stderr, _("Unable to cd to \"%s\"\n"), info->pw_dir); syslog(LOG_WARNING, "unable to cd to `%s' for user `%s'\n", info->pw_dir, info->pw_name); closelog(); exit (1); } puts(_("No directory, logging in with HOME=/")); info->pw_dir = temp_pw_dir; } /* * Create the HOME environmental variable and export it. */ addenv("HOME", info->pw_dir); /* * Create the SHELL environmental variable and export it. */ if (info->pw_shell == (char *) 0 || ! *info->pw_shell) { static char temp_pw_shell[] = "/bin/sh"; info->pw_shell = temp_pw_shell; } addenv("SHELL", info->pw_shell); /* * Create the PATH environmental variable and export it. */ cp = getdef_str("ENV_PATH"); addenv(cp ? cp : "PATH=/bin:/usr/bin", NULL); /* * Export the user name. For BSD derived systems, it's "USER", for * all others it's "LOGNAME". We set both of them. */ addenv("USER", info->pw_name); addenv("LOGNAME", info->pw_name); }
/* * Generate 8 base64 ASCII characters of random salt. If MD5_CRYPT_ENAB * in /etc/login.defs is "yes", the salt string will be prefixed by "$1$" * (magic) and pw_encrypt() will execute the MD5-based FreeBSD-compatible * version of crypt() instead of the standard one. * Other methods can be set with ENCRYPT_METHOD * * The method can be forced with the meth parameter. * If NULL, the method will be defined according to the MD5_CRYPT_ENAB and * ENCRYPT_METHOD login.defs variables. * * If meth is specified, an additional parameter can be provided. * * For the SHA256 and SHA512 method, this specifies the number of rounds * (if not NULL). */ /*@observer@*/const char *crypt_make_salt (/*@null@*/const char *meth, /*@null@*/void *arg) { /* Max result size for the SHA methods: * +3 $5$ * +17 rounds=999999999$ * +16 salt * +1 \0 */ static char result[40]; size_t salt_len = 8; const char *method; result[0] = '\0'; if (NULL != meth) method = meth; else { method = getdef_str ("ENCRYPT_METHOD"); if (NULL == method) { method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES"; } } if (0 == strcmp (method, "MD5")) { MAGNUM(result, '1'); #ifdef USE_SHA_CRYPT } else if (0 == strcmp (method, "SHA256")) { MAGNUM(result, '5'); strcat(result, SHA_salt_rounds((int *)arg)); salt_len = SHA_salt_size(); } else if (0 == strcmp (method, "SHA512")) { MAGNUM(result, '6'); strcat(result, SHA_salt_rounds((int *)arg)); salt_len = SHA_salt_size(); #endif /* USE_SHA_CRYPT */ } else if (0 != strcmp (method, "DES")) { fprintf (stderr, _("Invalid ENCRYPT_METHOD value: '%s'.\n" "Defaulting to DES.\n"), method); result[0] = '\0'; } /* * Concatenate a pseudo random salt. */ assert (sizeof (result) > strlen (result) + salt_len); strncat (result, gensalt (salt_len), sizeof (result) - strlen (result) - 1); return result; }
/* * get_failent_user - Return a string that can be used to log failure * from an user. * * This will be either the user argument, or "UNKNOWN". * * It is quite common to mistyped the password for username, and passwords * should not be logged. */ static /*@observer@*/const char *get_failent_user (/*@returned@*/const char *user) { const char *failent_user = "******"; bool log_unkfail_enab = getdef_bool("LOG_UNKFAIL_ENAB"); if ((NULL != user) && ('\0' != user[0])) { if ( log_unkfail_enab || (getpwnam (user) != NULL)) { failent_user = user; } } return failent_user; }
shadowtcb_status shadowtcb_drop_priv (void) { if (!getdef_bool ("USE_TCB")) { return SHADOWTCB_SUCCESS; } if (NULL != stored_tcb_user) { if (tcb_drop_priv (stored_tcb_user) == 0) { return SHADOWTCB_SUCCESS; } } return SHADOWTCB_FAILURE; }
static void su_failure (const char *tty) { sulog (tty, false, oldname, name); /* log failed attempt */ #ifdef USE_SYSLOG if (getdef_bool ("SYSLOG_SU_ENAB")) { SYSLOG (((0 != pwent.pw_uid) ? LOG_INFO : LOG_NOTICE, "- %s %s:%s", tty, ('\0' != oldname[0]) ? oldname : "???", ('\0' != name[0]) ? name : "???")); } closelog (); #endif exit (1); }
/* * usage - print syntax message and exit */ static void usage (void) { #ifdef WITH_TCB if (getdef_bool ("USE_TCB")) { fprintf (stderr, _("Usage: %s [-q] [-r] [passwd]\n"), Prog); } else #endif /* WITH_TCB */ { fprintf (stderr, _("Usage: %s [-q] [-r] [-s] [passwd [shadow]]\n"), Prog); } exit (E_USAGE); }
void mailcheck () { struct stat statbuf; char *mailbox; if (! getdef_bool("MAIL_CHECK_ENAB")) return; if (! (mailbox = getenv ("MAIL"))) return; if (stat (mailbox, &statbuf) == -1 || statbuf.st_size == 0) puts ("No mail."); else if (statbuf.st_atime > statbuf.st_mtime) puts ("You have mail."); else puts ("You have new mail."); }
int spw_close (void) { int retval = 0; #ifdef WITH_TCB bool use_tcb = getdef_bool ("USE_TCB"); if (use_tcb && (shadowtcb_drop_priv () == SHADOWTCB_FAILURE)) { return 0; } #endif /* WITH_TCB */ retval = commonio_close (&shadow_db); #ifdef WITH_TCB if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) { return 0; } #endif /* WITH_TCB */ return retval; }
static int remove_tcbdir (const char *user_name, uid_t user_id) { char *buf; int ret = 0; size_t bufsize = (sizeof TCB_DIR) + strlen (user_name) + 2; if (!getdef_bool ("USE_TCB")) { return 0; } buf = malloc (buflen); if (NULL == buf) { fprintf (stderr, _("%s: Can't allocate memory, " "tcb entry for %s not removed.\n"), Prog, user_name); return 1; } snprintf (buf, buflen, TCB_DIR "/%s", user_name); if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) { fprintf (stderr, _("%s: Cannot drop privileges: %s\n"), Prog, strerror (errno)); shadowtcb_gain_priv (); free (buf); return 1; } /* Only remove directory contents with dropped privileges. * We will regain them and remove the user's tcb directory afterwards. */ if (remove_tree (buf, false) != 0) { fprintf (stderr, _("%s: Cannot remove the content of %s: %s\n"), Prog, buf, strerror (errno)); shadowtcb_gain_priv (); free (buf); return 1; } shadowtcb_gain_priv (); free (buf); if (shadowtcb_remove (user_name) == SHADOWTCB_FAILURE) { fprintf (stderr, _("%s: Cannot remove tcb files for %s: %s\n"), Prog, user_name, strerror (errno)); ret = 1; } return ret; }
int spw_unlock (void) { #ifdef WITH_TCB int retval = 0; if (!getdef_bool ("USE_TCB")) { #endif /* WITH_TCB */ return commonio_unlock (&shadow_db); #ifdef WITH_TCB } if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) { return 0; } if (ulckpwdf_tcb () == 0) { shadow_db.locked = 0; retval = 1; } if (shadowtcb_gain_priv () == SHADOWTCB_FAILURE) { return 0; } return retval; #endif /* WITH_TCB */ }
/* * chfn - change a user's password file information * * This command controls the GECOS field information in the password * file entry. * * The valid options are * * -f full name * -r room number * -w work phone number * -h home phone number * -o other information (*) * * (*) requires root permission to execute. */ int main (int argc, char **argv) { char *cp; /* temporary character pointer */ const struct passwd *pw; /* password file entry */ struct passwd pwent; /* modified password file entry */ char old_gecos[BUFSIZ]; /* buffer for old GECOS fields */ char new_gecos[BUFSIZ]; /* buffer for new GECOS fields */ int flag; /* flag currently being processed */ int fflg = 0; /* -f - set full name */ int rflg = 0; /* -r - set room number */ int wflg = 0; /* -w - set work phone number */ int hflg = 0; /* -h - set home phone number */ int oflg = 0; /* -o - set other information */ char *user; #ifdef USE_PAM pam_handle_t *pamh = NULL; struct passwd *pampw; int retval; #endif sanitize_env (); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); /* * This command behaves different for root and non-root * users. */ amroot = (getuid () == 0); /* * Get the program name. The program name is used as a * prefix to most error messages. */ Prog = Basename (argv[0]); OPENLOG ("chfn"); /* * The remaining arguments will be processed one by one and executed * by this command. The name is the last argument if it does not * begin with a "-", otherwise the name is determined from the * environment and must agree with the real UID. Also, the UID will * be checked for any commands which are restricted to root only. */ while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) { switch (flag) { case 'f': if (!may_change_field ('f')) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } fflg++; STRFCPY (fullnm, optarg); break; case 'h': if (!may_change_field ('h')) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } hflg++; STRFCPY (homeph, optarg); break; case 'r': if (!may_change_field ('r')) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } rflg++; STRFCPY (roomno, optarg); break; case 'o': if (!amroot) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } oflg++; STRFCPY (slop, optarg); break; case 'w': if (!may_change_field ('w')) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } wflg++; STRFCPY (workph, optarg); break; default: usage (); } } /* * Get the name of the user to check. It is either the command line * name, or the name getlogin() returns. */ if (optind < argc) { user = argv[optind]; pw = getpwnam (user); if (!pw) { fprintf (stderr, _("%s: unknown user %s\n"), Prog, user); exit (E_NOPERM); } } else { pw = get_my_pwent (); if (!pw) { fprintf (stderr, _ ("%s: Cannot determine your user name.\n"), Prog); exit (E_NOPERM); } user = xstrdup (pw->pw_name); } #ifdef USE_NIS /* * Now we make sure this is a LOCAL password entry for this user ... */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: cannot change user `%s' on NIS client.\n"), Prog, user); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _ ("%s: `%s' is the NIS master for this client.\n"), Prog, nis_master); } exit (E_NOPERM); } #endif /* * Non-privileged users are only allowed to change the gecos field * if the UID of the user matches the current real UID. */ if (!amroot && pw->pw_uid != getuid ()) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); closelog (); exit (E_NOPERM); } #ifdef WITH_SELINUX /* * If the UID of the user does not match the current real UID, * check if the change is allowed by SELinux policy. */ if ((pw->pw_uid != getuid ()) && (selinux_check_passwd_access (PASSWD__CHFN) != 0)) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); closelog (); exit (E_NOPERM); } #endif #ifndef USE_PAM /* * Non-privileged users are optionally authenticated (must enter the * password of the user whose information is being changed) before * any changes can be made. Idea from util-linux chfn/chsh. * --marekm */ if (!amroot && getdef_bool ("CHFN_AUTH")) passwd_check (pw->pw_name, pw->pw_passwd, "chfn"); #else /* !USE_PAM */ retval = PAM_SUCCESS; pampw = getpwuid (getuid ()); if (pampw == NULL) { retval = PAM_USER_UNKNOWN; } if (retval == PAM_SUCCESS) { retval = pam_start ("chfn", 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 (E_NOPERM); } #endif /* USE_PAM */ /* * Now get the full name. It is the first comma separated field in * the GECOS field. */ STRFCPY (old_gecos, pw->pw_gecos); cp = copy_field (old_gecos, fflg ? (char *) 0 : fullnm, slop); /* * Now get the room number. It is the next comma separated field, * if there is indeed one. */ if (cp) cp = copy_field (cp, rflg ? (char *) 0 : roomno, slop); /* * Now get the work phone number. It is the third field. */ if (cp) cp = copy_field (cp, wflg ? (char *) 0 : workph, slop); /* * Now get the home phone number. It is the fourth field. */ if (cp) cp = copy_field (cp, hflg ? (char *) 0 : homeph, slop); /* * Anything left over is "slop". */ if (cp && !oflg) { if (slop[0]) strcat (slop, ","); strcat (slop, cp); } /* * If none of the fields were changed from the command line, let the * user interactively change them. */ if (!fflg && !rflg && !wflg && !hflg && !oflg) { printf (_("Changing the user information for %s\n"), user); new_fields (); } /* * Check all of the fields for valid information */ if (valid_field (fullnm, ":,=")) { fprintf (stderr, _("%s: invalid name: \"%s\"\n"), Prog, fullnm); closelog (); exit (E_NOPERM); } if (valid_field (roomno, ":,=")) { fprintf (stderr, _("%s: invalid room number: \"%s\"\n"), Prog, roomno); closelog (); exit (E_NOPERM); } if (valid_field (workph, ":,=")) { fprintf (stderr, _("%s: invalid work phone: \"%s\"\n"), Prog, workph); closelog (); exit (E_NOPERM); } if (valid_field (homeph, ":,=")) { fprintf (stderr, _("%s: invalid home phone: \"%s\"\n"), Prog, homeph); closelog (); exit (E_NOPERM); } if (valid_field (slop, ":")) { fprintf (stderr, _("%s: \"%s\" contains illegal characters\n"), Prog, slop); closelog (); exit (E_NOPERM); } /* * Build the new GECOS field by plastering all the pieces together, * if they will fit ... */ if (strlen (fullnm) + strlen (roomno) + strlen (workph) + strlen (homeph) + strlen (slop) > (unsigned int) 80) { fprintf (stderr, _("%s: fields too long\n"), Prog); closelog (); exit (E_NOPERM); } snprintf (new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s", fullnm, roomno, workph, homeph, slop[0] ? "," : "", slop); /* * Before going any further, raise the ulimit to prevent colliding * into a lowered ulimit, and set the real UID to root to protect * against unexpected signals. Any keyboard signals are set to be * ignored. */ if (setuid (0)) { fprintf (stderr, _("Cannot change ID to root.\n")); SYSLOG ((LOG_ERR, "can't setuid(0)")); closelog (); exit (E_NOPERM); } pwd_init (); /* * The passwd entry is now ready to be committed back to the * password file. Get a lock on the file and open it. */ if (!pw_lock ()) { fprintf (stderr, _ ("Cannot lock the password file; try again later.\n")); SYSLOG ((LOG_WARN, "can't lock /etc/passwd")); closelog (); exit (E_NOPERM); } if (!pw_open (O_RDWR)) { fprintf (stderr, _("Cannot open the password file.\n")); pw_unlock (); SYSLOG ((LOG_ERR, "can't open /etc/passwd")); closelog (); exit (E_NOPERM); } /* * Get the entry to update using pw_locate() - we want the real one * from /etc/passwd, not the one from getpwnam() which could contain * the shadow password if (despite the warnings) someone enables * AUTOSHADOW (or SHADOW_COMPAT in libc). --marekm */ pw = pw_locate (user); if (!pw) { pw_unlock (); fprintf (stderr, _("%s: %s not found in /etc/passwd\n"), Prog, user); exit (E_NOPERM); } /* * Make a copy of the entry, then change the gecos field. The other * fields remain unchanged. */ pwent = *pw; pwent.pw_gecos = new_gecos; /* * Update the passwd file entry. If there is a DBM file, update that * entry as well. */ if (!pw_update (&pwent)) { fprintf (stderr, _("Error updating the password entry.\n")); pw_unlock (); SYSLOG ((LOG_ERR, "error updating passwd entry")); closelog (); exit (E_NOPERM); } /* * Changes have all been made, so commit them and unlock the file. */ if (!pw_close ()) { fprintf (stderr, _("Cannot commit password file changes.\n")); pw_unlock (); SYSLOG ((LOG_ERR, "can't rewrite /etc/passwd")); closelog (); exit (E_NOPERM); } if (!pw_unlock ()) { fprintf (stderr, _("Cannot unlock the password file.\n")); SYSLOG ((LOG_ERR, "can't unlock /etc/passwd")); closelog (); exit (E_NOPERM); } SYSLOG ((LOG_INFO, "changed user `%s' information", user)); nscd_flush_cache ("passwd"); #ifdef USE_PAM if (retval == PAM_SUCCESS) pam_end (pamh, PAM_SUCCESS); #endif /* USE_PAM */ closelog (); exit (E_SUCCESS); }
shadowtcb_status shadowtcb_create (const char *name, uid_t uid) { char *dir, *shadow; struct stat tcbdir_stat; gid_t shadowgid, authgid; struct group *gr; int fd; shadowtcb_status ret = SHADOWTCB_FAILURE; if (!getdef_bool ("USE_TCB")) { return SHADOWTCB_SUCCESS; } if (stat (TCB_DIR, &tcbdir_stat) != 0) { fprintf (stderr, _("%s: Cannot stat %s: %s\n"), Prog, TCB_DIR, strerror (errno)); return SHADOWTCB_FAILURE; } shadowgid = tcbdir_stat.st_gid; authgid = shadowgid; if (getdef_bool ("TCB_AUTH_GROUP")) { gr = getgrnam ("auth"); if (NULL != gr) { authgid = gr->gr_gid; } } if ( (asprintf (&dir, TCB_DIR "/%s", name) == -1) || (asprintf (&shadow, TCB_FMT, name) == -1)) { OUT_OF_MEMORY; return SHADOWTCB_FAILURE; } if (mkdir (dir, 0700) != 0) { fprintf (stderr, _("%s: mkdir: %s: %s\n"), Prog, dir, strerror (errno)); goto out_free; } fd = open (shadow, O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd < 0) { fprintf (stderr, _("%s: Cannot open %s: %s\n"), Prog, shadow, strerror (errno)); goto out_free; } close (fd); if (chown (shadow, 0, authgid) != 0) { fprintf (stderr, _("%s: Cannot change owner of %s: %s\n"), Prog, shadow, strerror (errno)); goto out_free; } if (chmod (shadow, (mode_t) ((authgid == shadowgid) ? 0600 : 0640)) != 0) { fprintf (stderr, _("%s: Cannot change mode of %s: %s\n"), Prog, shadow, strerror (errno)); goto out_free; } if (chown (dir, 0, authgid) != 0) { fprintf (stderr, _("%s: Cannot change owner of %s: %s\n"), Prog, dir, strerror (errno)); goto out_free; } if (chmod (dir, (mode_t) ((authgid == shadowgid) ? 02700 : 02710)) != 0) { fprintf (stderr, _("%s: Cannot change mode of %s: %s\n"), Prog, dir, strerror (errno)); goto out_free; } if ( (shadowtcb_set_user (name) == SHADOWTCB_FAILURE) || (shadowtcb_move (NULL, uid) == SHADOWTCB_FAILURE)) { goto out_free; } ret = SHADOWTCB_SUCCESS; out_free: free (dir); free (shadow); return ret; }
/* * check_perms - check if the caller is allowed to add a group * * Non-root users are only allowed to change their gecos field. * (see also may_change_field()) * * Non-root users must be authenticated. * * It will not return if the user is not allowed. */ static void check_perms (const struct passwd *pw) { #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; struct passwd *pampw; #endif /* * Non-privileged users are only allowed to change the gecos field * if the UID of the user matches the current real UID. */ if (!amroot && pw->pw_uid != getuid ()) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); closelog (); exit (E_NOPERM); } #ifdef WITH_SELINUX /* * If the UID of the user does not match the current real UID, * check if the change is allowed by SELinux policy. */ if ((pw->pw_uid != getuid ()) && (is_selinux_enabled () > 0) && (selinux_check_passwd_access (PASSWD__CHFN) != 0)) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); closelog (); exit (E_NOPERM); } #endif #ifndef USE_PAM /* * Non-privileged users are optionally authenticated (must enter the * password of the user whose information is being changed) before * any changes can be made. Idea from util-linux chfn/chsh. * --marekm */ if (!amroot && getdef_bool ("CHFN_AUTH")) { passwd_check (pw->pw_name, pw->pw_passwd, "chfn"); } #else /* !USE_PAM */ pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */ if (NULL == pampw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); exit (E_NOPERM); } retval = pam_start ("chfn", 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_NOPERM); } (void) pam_end (pamh, retval); #endif /* USE_PAM */ }
int get_passwd(char *bufp, int type) { char pwbuf[PWLEN + 1], pwbuf2[PWLEN + 1]; char salt[3]; int counter; int insistant = getdef_int("INSISTANT"); char *p; int got_password = 0; counter = 0; if (insistant < 1) insistant = 1; while (counter <= insistant) { if (getuid() == 0 || getdef_bool("NOPASSWORDOK")) { counter++; } pwbuf[0] = '\0'; pwbuf2[0] = '\0'; if ((p = getpass("New password:"******"NOPASSWORDOK")) { fprintf(stderr, "You must enter a password, please try again\n"); continue; } if (getuid() != 0 && getdef_bool("STRICTPASSWORD") && (strlen(pwbuf) < MIN_PWLEN || analyzepw(pwbuf, MIN_PWLEN) < TOO_SIMPLE)) { fprintf(stderr,too_simple_fmt, MIN_PWLEN, TOO_SIMPLE); continue; } pwbuf2[0] = '\0'; if ((p = getpass("Retype new password:"******"Mismatch - password not changed\n"); counter = 0; continue; } got_password = 1; break; } if (!got_password) { fprintf(stderr, "No password was provided\n"); return -1; } *bufp = 0; new_salt(salt); if (strlen(pwbuf) > 0) { #if defined(__QNXNTO__) if (!(type & (USE_QNX_CRYPT | USE_UNIX_CRYPT))) { if (getdef_bool("QNXCRYPT")) { type |= USE_QNX_CRYPT; } else { type |= USE_UNIX_CRYPT; } } if (type & USE_QNX_CRYPT) { strncpy(bufp, qnx_crypt(pwbuf, salt), PWLEN); bufp[PWLEN] = '\0'; } else if (type & USE_UNIX_CRYPT) { strncpy(bufp, crypt(pwbuf, salt), PWLEN); bufp[PWLEN] = '\0'; } #else strncpy(bufp, crypt(pwbuf, salt), PWLEN); bufp[PWLEN] = '\0'; #endif } memset(pwbuf, 0, sizeof pwbuf); memset(pwbuf2, 0, sizeof pwbuf2); return *bufp ? 1 : 0; }
int main (int argc, char **argv) { const struct passwd *pw; struct passwd pwent; const struct spwd *sp; struct spwd spent; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("pwconv"); process_flags (argc, argv); #ifdef WITH_TCB if (getdef_bool("USE_TCB")) { fprintf (stderr, _("%s: can't work with tcb enabled\n"), Prog); exit (E_FAILURE); } #endif /* WITH_TCB */ if (pw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, pw_dbname ()); fail_exit (E_PWDBUSY); } pw_locked = true; if (pw_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); fail_exit (E_MISSING); } if (spw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, spw_dbname ()); fail_exit (E_PWDBUSY); } spw_locked = true; if (spw_open (O_CREAT | O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); fail_exit (E_FAILURE); } /* * Remove /etc/shadow entries for users not in /etc/passwd. */ (void) spw_rewind (); while ((sp = spw_next ()) != NULL) { if (pw_locate (sp->sp_namp) != NULL) { continue; } if (spw_remove (sp->sp_namp) == 0) { /* * This shouldn't happen (the entry exists) but... */ fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, sp->sp_namp, spw_dbname ()); fail_exit (E_FAILURE); } } /* * Update shadow entries which don't have "x" as pw_passwd. Add any * missing shadow entries. */ (void) pw_rewind (); while ((pw = pw_next ()) != NULL) { sp = spw_locate (pw->pw_name); if (NULL != sp) { /* do we need to update this entry? */ if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) { continue; } /* update existing shadow entry */ spent = *sp; } else { /* add new shadow entry */ memset (&spent, 0, sizeof spent); spent.sp_namp = pw->pw_name; spent.sp_min = getdef_num ("PASS_MIN_DAYS", -1); spent.sp_max = getdef_num ("PASS_MAX_DAYS", -1); spent.sp_warn = getdef_num ("PASS_WARN_AGE", -1); spent.sp_inact = -1; spent.sp_expire = -1; spent.sp_flag = SHADOW_SP_FLAG_UNSET; } spent.sp_pwdp = pw->pw_passwd; spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE; if (0 == spent.sp_lstchg) { /* Better disable aging than requiring a password * change */ spent.sp_lstchg = -1; } if (spw_update (&spent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, spw_dbname (), spent.sp_namp); fail_exit (E_FAILURE); } /* remove password from /etc/passwd */ pwent = *pw; pwent.pw_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */ if (pw_update (&pwent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pwent.pw_name); fail_exit (E_FAILURE); } } if (spw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); fail_exit (E_FAILURE); } if (pw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); fail_exit (E_FAILURE); } /* /etc/passwd- (backup file) */ if (chmod (PASSWD_FILE "-", 0600) != 0) { fprintf (stderr, _("%s: failed to change the mode of %s to 0600\n"), Prog, PASSWD_FILE "-"); SYSLOG ((LOG_ERR, "failed to change the mode of %s to 0600", PASSWD_FILE "-")); /* continue */ } if (pw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); /* continue */ } if (spw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); /* continue */ } nscd_flush_cache ("passwd"); return E_SUCCESS; }
/* * login - create a new login session for a user * * login is typically called by getty as the second step of a * new user session. getty is responsible for setting the line * characteristics to a reasonable set of values and getting * the name of the user to be logged in. login may also be * called to create a new user session on a pty for a variety * of reasons, such as X servers or network logins. * * the flags which login supports are * * -p - preserve the environment * -r - perform autologin protocol for rlogin * -f - do not perform authentication, user is preauthenticated * -h - the name of the remote host */ int main (int argc, char **argv) { const char *tmptty; char tty[BUFSIZ]; #ifdef RLOGIN char term[128] = ""; #endif /* RLOGIN */ #if defined(HAVE_STRFTIME) && !defined(USE_PAM) char ptime[80]; #endif unsigned int delay; unsigned int retries; bool failed; bool subroot = false; #ifndef USE_PAM bool is_console; #endif int err; const char *cp; char *tmp; char fromhost[512]; struct passwd *pwd = NULL; char **envp = environ; const char *failent_user; /*@null@*/struct utmp *utent; #ifdef USE_PAM int retcode; pid_t child; char *pam_user = NULL; #else struct spwd *spwd = NULL; #endif /* * Some quick initialization. */ sanitize_env (); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); initenv (); amroot = (getuid () == 0); Prog = Basename (argv[0]); if (geteuid() != 0) { fprintf (stderr, _("%s: Cannot possibly work without effective root\n"), Prog); exit (1); } process_flags (argc, argv); if ((isatty (0) == 0) || (isatty (1) == 0) || (isatty (2) == 0)) { exit (1); /* must be a terminal */ } utent = get_current_utmp (); /* * Be picky if run by normal users (possible if installed setuid * root), but not if run by root. This way it still allows logins * even if your getty is broken, or if something corrupts utmp, * but users must "exec login" which will use the existing utmp * entry (will not overwrite remote hostname). --marekm */ if (!amroot && (NULL == utent)) { (void) puts (_("No utmp entry. You must exec \"login\" from the lowest level \"sh\"")); exit (1); } /* NOTE: utent might be NULL afterwards */ tmptty = ttyname (0); if (NULL == tmptty) { tmptty = "UNKNOWN"; } STRFCPY (tty, tmptty); #ifndef USE_PAM is_console = console (tty); #endif if (rflg || hflg) { /* * Add remote hostname to the environment. I think * (not sure) I saw it once on Irix. --marekm */ addenv ("REMOTEHOST", hostname); } if (fflg) { preauth_flag = true; } if (hflg) { reason = PW_RLOGIN; } #ifdef RLOGIN if (rflg) { assert (NULL == username); username = xmalloc (USER_NAME_MAX_LENGTH + 1); username[USER_NAME_MAX_LENGTH] = '\0'; if (do_rlogin (hostname, username, USER_NAME_MAX_LENGTH, term, sizeof term)) { preauth_flag = true; } else { free (username); username = NULL; } } #endif /* RLOGIN */ OPENLOG ("login"); setup_tty (); #ifndef USE_PAM (void) umask (getdef_num ("UMASK", GETDEF_DEFAULT_UMASK)); { /* * Use the ULIMIT in the login.defs file, and if * there isn't one, use the default value. The * user may have one for themselves, but otherwise, * just take what you get. */ long limit = getdef_long ("ULIMIT", -1L); if (limit != -1) { set_filesize_limit (limit); } } #endif /* * The entire environment will be preserved if the -p flag * is used. */ if (pflg) { while (NULL != *envp) { /* add inherited environment, */ addenv (*envp, NULL); /* some variables change later */ envp++; } } #ifdef RLOGIN if (term[0] != '\0') { addenv ("TERM", term); } else #endif /* RLOGIN */ { /* preserve TERM from getty */ if (!pflg) { tmp = getenv ("TERM"); if (NULL != tmp) { addenv ("TERM", tmp); } } } init_env (); if (optind < argc) { /* now set command line variables */ set_env (argc - optind, &argv[optind]); } if (rflg || hflg) { cp = hostname; #ifdef HAVE_STRUCT_UTMP_UT_HOST } else if ((NULL != utent) && ('\0' != utent->ut_host[0])) { cp = utent->ut_host; #endif /* HAVE_STRUCT_UTMP_UT_HOST */ } else { cp = ""; } if ('\0' != *cp) { snprintf (fromhost, sizeof fromhost, " on '%.100s' from '%.200s'", tty, cp); } else { snprintf (fromhost, sizeof fromhost, " on '%.100s'", tty); } top: /* only allow ALARM sec. for login */ (void) signal (SIGALRM, alarm_handler); timeout = getdef_unum ("LOGIN_TIMEOUT", ALARM); if (timeout > 0) { (void) alarm (timeout); } environ = newenvp; /* make new environment active */ delay = getdef_unum ("FAIL_DELAY", 1); retries = getdef_unum ("LOGIN_RETRIES", RETRIES); #ifdef USE_PAM retcode = pam_start ("login", username, &conv, &pamh); if (retcode != PAM_SUCCESS) { fprintf (stderr, _("login: PAM Failure, aborting: %s\n"), pam_strerror (pamh, retcode)); SYSLOG ((LOG_ERR, "Couldn't initialize PAM: %s", pam_strerror (pamh, retcode))); exit (99); } /* * hostname & tty are either set to NULL or their correct values, * depending on how much we know. We also set PAM's fail delay to * ours. * * PAM_RHOST and PAM_TTY are used for authentication, only use * information coming from login or from the caller (e.g. no utmp) */ retcode = pam_set_item (pamh, PAM_RHOST, hostname); PAM_FAIL_CHECK; retcode = pam_set_item (pamh, PAM_TTY, tty); PAM_FAIL_CHECK; #ifdef HAS_PAM_FAIL_DELAY retcode = pam_fail_delay (pamh, 1000000 * delay); PAM_FAIL_CHECK; #endif /* if fflg, then the user has already been authenticated */ if (!fflg) { unsigned int failcount = 0; char hostn[256]; char loginprompt[256]; /* That's one hell of a prompt :) */ /* Make the login prompt look like we want it */ if (gethostname (hostn, sizeof (hostn)) == 0) { snprintf (loginprompt, sizeof (loginprompt), _("%s login: "******"login: "******"TOO MANY LOGIN TRIES (%u)%s FOR '%s'", failcount, fromhost, failent_user)); fprintf(stderr, _("Maximum number of tries exceeded (%u)\n"), failcount); PAM_END; exit(0); } else if (retcode == PAM_ABORT) { /* Serious problems, quit now */ (void) fputs (_("login: abort requested by PAM\n"), stderr); SYSLOG ((LOG_ERR,"PAM_ABORT returned from pam_authenticate()")); PAM_END; exit(99); } else if (retcode != PAM_SUCCESS) { SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%u)%s FOR '%s', %s", failcount, fromhost, failent_user, pam_strerror (pamh, retcode))); failed = true; } if (!failed) { break; } #ifdef WITH_AUDIT audit_fd = audit_open (); audit_log_acct_message (audit_fd, AUDIT_USER_LOGIN, NULL, /* Prog. name */ "login", failent_user, AUDIT_NO_ID, hostname, NULL, /* addr */ tty, 0); /* result */ close (audit_fd); #endif /* WITH_AUDIT */ (void) puts (""); (void) puts (_("Login incorrect")); if (failcount >= retries) { SYSLOG ((LOG_NOTICE, "TOO MANY LOGIN TRIES (%u)%s FOR '%s'", failcount, fromhost, failent_user)); fprintf(stderr, _("Maximum number of tries exceeded (%u)\n"), failcount); PAM_END; exit(0); } /* * Let's give it another go around. * Even if a username was given on the command * line, prompt again for the username. */ retcode = pam_set_item (pamh, PAM_USER, NULL); PAM_FAIL_CHECK; } /* We don't get here unless they were authenticated above */ (void) alarm (0); } /* Check the account validity */ retcode = pam_acct_mgmt (pamh, 0); if (retcode == PAM_NEW_AUTHTOK_REQD) { retcode = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); } PAM_FAIL_CHECK; /* Open the PAM session */ get_pam_user (&pam_user); retcode = pam_open_session (pamh, hushed (pam_user) ? PAM_SILENT : 0); PAM_FAIL_CHECK; /* Grab the user information out of the password file for future usage * First get the username that we are actually using, though. * * From now on, we will discard changes of the user (PAM_USER) by * PAM APIs. */ get_pam_user (&pam_user); if (NULL != username) { free (username); } username = pam_user; failent_user = get_failent_user (username); pwd = xgetpwnam (username); if (NULL == pwd) { SYSLOG ((LOG_ERR, "cannot find user %s", failent_user)); exit (1); } /* This set up the process credential (group) and initialize the * supplementary group access list. * This has to be done before pam_setcred */ if (setup_groups (pwd) != 0) { exit (1); } retcode = pam_setcred (pamh, PAM_ESTABLISH_CRED); PAM_FAIL_CHECK; /* NOTE: If pam_setcred changes PAM_USER, this will not be taken * into account. */ #else /* ! USE_PAM */ while (true) { /* repeatedly get login/password pairs */ /* user_passwd is always a pointer to this constant string * or a passwd or shadow password that will be memzero by * pw_free / spw_free. * Do not free() user_passwd. */ const char *user_passwd = "!"; /* Do some cleanup to avoid keeping entries we do not need * anymore. */ if (NULL != pwd) { pw_free (pwd); pwd = NULL; } if (NULL != spwd) { spw_free (spwd); spwd = NULL; } failed = false; /* haven't failed authentication yet */ if (NULL == username) { /* need to get a login id */ if (subroot) { closelog (); exit (1); } preauth_flag = false; username = xmalloc (USER_NAME_MAX_LENGTH + 1); username[USER_NAME_MAX_LENGTH] = '\0'; login_prompt (_("\n%s login: "******"!", * the account is locked and the user cannot * login, even if they have been * "pre-authenticated." */ if ( ('!' == user_passwd[0]) || ('*' == user_passwd[0])) { failed = true; } } if (strcmp (user_passwd, SHADOW_PASSWD_STRING) == 0) { spwd = xgetspnam (username); if (NULL != spwd) { user_passwd = spwd->sp_pwdp; } else { /* The user exists in passwd, but not in * shadow. SHADOW_PASSWD_STRING indicates * that the password shall be in shadow. */ SYSLOG ((LOG_WARN, "no shadow password for '%s'%s", username, fromhost)); } } /* * The -r and -f flags provide a name which has already * been authenticated by some server. */ if (preauth_flag) { goto auth_ok; } if (pw_auth (user_passwd, username, reason, (char *) 0) == 0) { goto auth_ok; } SYSLOG ((LOG_WARN, "invalid password for '%s' %s", failent_user, fromhost)); failed = true; auth_ok: /* * This is the point where all authenticated users wind up. * If you reach this far, your password has been * authenticated and so on. */ if ( !failed && (NULL != pwd) && (0 == pwd->pw_uid) && !is_console) { SYSLOG ((LOG_CRIT, "ILLEGAL ROOT LOGIN %s", fromhost)); failed = true; } if ( !failed && !login_access (username, ('\0' != *hostname) ? hostname : tty)) { SYSLOG ((LOG_WARN, "LOGIN '%s' REFUSED %s", username, fromhost)); failed = true; } if ( (NULL != pwd) && getdef_bool ("FAILLOG_ENAB") && !failcheck (pwd->pw_uid, &faillog, failed)) { SYSLOG ((LOG_CRIT, "exceeded failure limit for '%s' %s", username, fromhost)); failed = true; } if (!failed) { break; } /* don't log non-existent users */ if ((NULL != pwd) && getdef_bool ("FAILLOG_ENAB")) { failure (pwd->pw_uid, tty, &faillog); } if (getdef_str ("FTMP_FILE") != NULL) { #ifdef USE_UTMPX struct utmpx *failent = prepare_utmpx (failent_user, tty, /* FIXME: or fromhost? */hostname, utent); #else /* !USE_UTMPX */ struct utmp *failent = prepare_utmp (failent_user, tty, hostname, utent); #endif /* !USE_UTMPX */ failtmp (failent_user, failent); free (failent); } retries--; if (retries <= 0) { SYSLOG ((LOG_CRIT, "REPEATED login failures%s", fromhost)); } /* * If this was a passwordless account and we get here, login * was denied (securetty, faillog, etc.). There was no * password prompt, so do it now (will always fail - the bad * guys won't see that the passwordless account exists at * all). --marekm */ if (user_passwd[0] == '\0') { pw_auth ("!", username, reason, (char *) 0); } /* * Authentication of this user failed. * The username must be confirmed in the next try. */ free (username); username = NULL; /* * Wait a while (a la SVR4 /usr/bin/login) before attempting * to login the user again. If the earlier alarm occurs * before the sleep() below completes, login will exit. */ if (delay > 0) { (void) sleep (delay); } (void) puts (_("Login incorrect")); /* allow only one attempt with -r or -f */ if (rflg || fflg || (retries <= 0)) { closelog (); exit (1); } } /* while (true) */ #endif /* ! USE_PAM */ assert (NULL != username); assert (NULL != pwd); (void) alarm (0); /* turn off alarm clock */ #ifndef USE_PAM /* PAM does this */ /* * porttime checks moved here, after the user has been * authenticated. now prints a message, as suggested * by Ivan Nejgebauer <*****@*****.**>. --marekm */ if ( getdef_bool ("PORTTIME_CHECKS_ENAB") && !isttytime (username, tty, time ((time_t *) 0))) { SYSLOG ((LOG_WARN, "invalid login time for '%s'%s", username, fromhost)); closelog (); bad_time_notify (); exit (1); } check_nologin (pwd->pw_uid == 0); #endif if (getenv ("IFS")) { /* don't export user IFS ... */ addenv ("IFS= \t\n", NULL); /* ... instead, set a safe IFS */ } if (pwd->pw_shell[0] == '*') { /* subsystem root */ pwd->pw_shell++; /* skip the '*' */ subsystem (pwd); /* figure out what to execute */ subroot = true; /* say I was here again */ endpwent (); /* close all of the file which were */ endgrent (); /* open in the original rooted file */ endspent (); /* system. they will be re-opened */ #ifdef SHADOWGRP endsgent (); /* in the new rooted file system */ #endif goto top; /* go do all this all over again */ } #ifdef WITH_AUDIT audit_fd = audit_open (); audit_log_acct_message (audit_fd, AUDIT_USER_LOGIN, NULL, /* Prog. name */ "login", username, AUDIT_NO_ID, hostname, NULL, /* addr */ tty, 1); /* result */ close (audit_fd); #endif /* WITH_AUDIT */ #ifndef USE_PAM /* pam_lastlog handles this */ if (getdef_bool ("LASTLOG_ENAB")) { /* give last login and log this one */ dolastlog (&ll, pwd, tty, hostname); } #endif #ifndef USE_PAM /* PAM handles this as well */ /* * Have to do this while we still have root privileges, otherwise we * don't have access to /etc/shadow. */ if (NULL != spwd) { /* check for age of password */ if (expire (pwd, spwd)) { /* The user updated her password, get the new * entries. * Use the x variants because we need to keep the * entry for a long time, and there might be other * getxxyy in between. */ pw_free (pwd); pwd = xgetpwnam (username); if (NULL == pwd) { SYSLOG ((LOG_ERR, "cannot find user %s after update of expired password", username)); exit (1); } spw_free (spwd); spwd = xgetspnam (username); } } setup_limits (pwd); /* nice, ulimit etc. */ #endif /* ! USE_PAM */ chown_tty (pwd); #ifdef USE_PAM /* * We must fork before setuid() because we need to call * pam_close_session() as root. */ (void) signal (SIGINT, SIG_IGN); child = fork (); if (child < 0) { /* error in fork() */ fprintf (stderr, _("%s: failure forking: %s"), Prog, strerror (errno)); PAM_END; exit (0); } else if (child != 0) { /* * parent - wait for child to finish, then cleanup * session */ wait (NULL); PAM_END; exit (0); } /* child */ #endif /* If we were init, we need to start a new session */ if (getppid() == 1) { setsid(); if (ioctl(0, TIOCSCTTY, 1) != 0) { fprintf (stderr, _("TIOCSCTTY failed on %s"), tty); } } /* * The utmp entry needs to be updated to indicate the new status * of the session, the new PID and SID. */ update_utmp (username, tty, hostname, utent); /* The pwd and spwd entries for the user have been copied. * * Close all the files so that unauthorized access won't occur. */ endpwent (); /* stop access to password file */ endgrent (); /* stop access to group file */ endspent (); /* stop access to shadow passwd file */ #ifdef SHADOWGRP endsgent (); /* stop access to shadow group file */ #endif /* Drop root privileges */ #ifndef USE_PAM if (setup_uid_gid (pwd, is_console)) #else /* The group privileges were already dropped. * See setup_groups() above. */ if (change_uid (pwd)) #endif { exit (1); } setup_env (pwd); /* set env vars, cd to the home dir */ #ifdef USE_PAM { const char *const *env; env = (const char *const *) pam_getenvlist (pamh); while ((NULL != env) && (NULL != *env)) { addenv (*env, NULL); env++; } } #endif (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); if (!hushed (username)) { addenv ("HUSHLOGIN=FALSE", NULL); /* * pam_unix, pam_mail and pam_lastlog should take care of * this */ #ifndef USE_PAM motd (); /* print the message of the day */ if ( getdef_bool ("FAILLOG_ENAB") && (0 != faillog.fail_cnt)) { failprint (&faillog); /* Reset the lockout times if logged in */ if ( (0 != faillog.fail_max) && (faillog.fail_cnt >= faillog.fail_max)) { (void) puts (_("Warning: login re-enabled after temporary lockout.")); SYSLOG ((LOG_WARN, "login '%s' re-enabled after temporary lockout (%d failures)", username, (int) faillog.fail_cnt)); } } if ( getdef_bool ("LASTLOG_ENAB") && (ll.ll_time != 0)) { time_t ll_time = ll.ll_time; #ifdef HAVE_STRFTIME (void) strftime (ptime, sizeof (ptime), "%a %b %e %H:%M:%S %z %Y", localtime (&ll_time)); printf (_("Last login: %s on %s"), ptime, ll.ll_line); #else printf (_("Last login: %.19s on %s"), ctime (&ll_time), ll.ll_line); #endif #ifdef HAVE_LL_HOST /* __linux__ || SUN4 */ if ('\0' != ll.ll_host[0]) { printf (_(" from %.*s"), (int) sizeof ll.ll_host, ll.ll_host); } #endif printf (".\n"); } agecheck (spwd); mailcheck (); /* report on the status of mail */ #endif /* !USE_PAM */ } else { addenv ("HUSHLOGIN=TRUE", NULL); } ttytype (tty); (void) signal (SIGQUIT, SIG_DFL); /* default quit signal */ (void) signal (SIGTERM, SIG_DFL); /* default terminate signal */ (void) signal (SIGALRM, SIG_DFL); /* default alarm signal */ (void) signal (SIGHUP, SIG_DFL); /* added this. --marekm */ (void) signal (SIGINT, SIG_DFL); /* default interrupt signal */ if (0 == pwd->pw_uid) { SYSLOG ((LOG_NOTICE, "ROOT LOGIN %s", fromhost)); } else if (getdef_bool ("LOG_OK_LOGINS")) { SYSLOG ((LOG_INFO, "'%s' logged in %s", username, fromhost)); } closelog (); tmp = getdef_str ("FAKE_SHELL"); if (NULL != tmp) { err = shell (tmp, pwd->pw_shell, newenvp); /* fake shell */ } else { /* exec the shell finally */ err = shell (pwd->pw_shell, (char *) 0, newenvp); } return ((err == ENOENT) ? E_CMD_NOTFOUND : E_CMD_NOEXEC); }
/* * update_groups - delete user from secondary group set * * update_groups() takes the user name that was given and searches * the group files for membership in any group. * * we also check to see if they have any groups they own (the same * name is their user name) and delete them too (only if USERGROUPS_ENAB * is enabled). */ static void update_groups (void) { const struct group *grp; struct group *ngrp; #ifdef SHADOWGRP const struct sgrp *sgrp; struct sgrp *nsgrp; #endif /* SHADOWGRP */ /* * Scan through the entire group file looking for the groups that * the user is a member of. */ for (gr_rewind (), grp = gr_next (); NULL != grp; grp = gr_next ()) { /* * See if the user specified this group as one of their * concurrent groups. */ if (!is_on_list (grp->gr_mem, user_name)) { continue; } /* * Delete the username from the list of group members and * update the group entry to reflect the change. */ ngrp = __gr_dup (grp); if (NULL == ngrp) { fprintf (stderr, _("%s: Out of memory. Cannot update %s.\n"), Prog, gr_dbname ()); exit (13); /* XXX */ } ngrp->gr_mem = del_list (ngrp->gr_mem, user_name); if (gr_update (ngrp) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, gr_dbname (), ngrp->gr_name); exit (E_GRP_UPDATE); } /* * Update the DBM group file with the new entry as well. */ #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, "deleting user from group", user_name, (unsigned int) user_id, SHADOW_AUDIT_SUCCESS); #endif /* WITH_AUDIT */ SYSLOG ((LOG_INFO, "delete '%s' from group '%s'\n", user_name, ngrp->gr_name)); } if (getdef_bool ("USERGROUPS_ENAB")) { remove_usergroup (); } #ifdef SHADOWGRP if (!is_shadow_grp) { return; } /* * Scan through the entire shadow group file looking for the groups * that the user is a member of. Both the administrative list and * the ordinary membership list is checked. */ for (sgr_rewind (), sgrp = sgr_next (); NULL != sgrp; sgrp = sgr_next ()) { bool was_member, was_admin; /* * See if the user specified this group as one of their * concurrent groups. */ was_member = is_on_list (sgrp->sg_mem, user_name); was_admin = is_on_list (sgrp->sg_adm, user_name); if (!was_member && !was_admin) { continue; } nsgrp = __sgr_dup (sgrp); if (NULL == nsgrp) { fprintf (stderr, _("%s: Out of memory. Cannot update %s.\n"), Prog, sgr_dbname ()); exit (13); /* XXX */ } if (was_member) { nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name); } if (was_admin) { nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name); } if (sgr_update (nsgrp) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, sgr_dbname (), nsgrp->sg_name); exit (E_GRP_UPDATE); } #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, "deleting user from shadow group", user_name, (unsigned int) user_id, SHADOW_AUDIT_SUCCESS); #endif /* WITH_AUDIT */ SYSLOG ((LOG_INFO, "delete '%s' from shadow group '%s'\n", user_name, nsgrp->sg_name)); } #endif /* SHADOWGRP */ }
int main(int argc, char **argv) { char *cp; /* temporary character pointer */ const struct passwd *pw; /* password file entry */ struct passwd pwent; /* modified password file entry */ char old_gecos[BUFSIZ]; /* buffer for old GECOS fields */ char new_gecos[BUFSIZ]; /* buffer for new GECOS fields */ int flag; /* flag currently being processed */ int fflg = 0; /* -f - set full name */ int rflg = 0; /* -r - set room number */ int wflg = 0; /* -w - set work phone number */ int hflg = 0; /* -h - set home phone number */ int oflg = 0; /* -o - set other information */ char *user; sanitize_env(); setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); /* * This command behaves different for root and non-root * users. */ amroot = (getuid () == 0); #ifdef NDBM pw_dbm_mode = O_RDWR; #endif /* * Get the program name. The program name is used as a * prefix to most error messages. It is also used as input * to the openlog() function for error logging. */ Prog = Basename(argv[0]); openlog("chfn", LOG_PID, LOG_AUTH); /* * The remaining arguments will be processed one by one and * executed by this command. The name is the last argument * if it does not begin with a "-", otherwise the name is * determined from the environment and must agree with the * real UID. Also, the UID will be checked for any commands * which are restricted to root only. */ while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) { switch (flag) { case 'f': if (!may_change_field('f')) { fprintf(stderr, _("%s: Permission denied.\n"), Prog); exit(1); } fflg++; STRFCPY(fullnm, optarg); break; case 'r': if (!may_change_field('r')) { fprintf(stderr, _("%s: Permission denied.\n"), Prog); exit(1); } rflg++; STRFCPY(roomno, optarg); break; case 'w': if (!may_change_field('w')) { fprintf(stderr, _("%s: Permission denied.\n"), Prog); exit(1); } wflg++; STRFCPY(workph, optarg); break; case 'h': if (!may_change_field('h')) { fprintf(stderr, _("%s: Permission denied.\n"), Prog); exit(1); } hflg++; STRFCPY(homeph, optarg); break; case 'o': if (!amroot) { fprintf(stderr, _("%s: Permission denied.\n"), Prog); exit(1); } oflg++; STRFCPY(slop, optarg); break; default: usage(); } } /* * Get the name of the user to check. It is either * the command line name, or the name getlogin() * returns. */ if (optind < argc) { user = argv[optind]; pw = getpwnam(user); if (!pw) { fprintf(stderr, _("%s: Unknown user %s\n"), Prog, user); exit(1); } } else { pw = get_my_pwent(); if (!pw) { fprintf(stderr, _("%s: Cannot determine your user name.\n"), Prog); exit(1); } user = xstrdup(pw->pw_name); } #ifdef USE_NIS /* * Now we make sure this is a LOCAL password entry for * this user ... */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: cannot change user `%s' on NIS client.\n"), Prog, user); if (! yp_get_default_domain (&nis_domain) && ! yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _("%s: `%s' is the NIS master for this client.\n"), Prog, nis_master); } exit (1); } #endif /* * Non-privileged users are only allowed to change the * gecos field if the UID of the user matches the current * real UID. */ if (!amroot && pw->pw_uid != getuid()) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); closelog(); exit(1); } /* * Non-privileged users are optionally authenticated * (must enter the password of the user whose information * is being changed) before any changes can be made. * Idea from util-linux chfn/chsh. --marekm */ if (!amroot && getdef_bool("CHFN_AUTH")) passwd_check(pw->pw_name, pw->pw_passwd, "chfn"); /* * Now get the full name. It is the first comma separated field * in the GECOS field. */ STRFCPY(old_gecos, pw->pw_gecos); cp = copy_field (old_gecos, fflg ? (char *) 0:fullnm, slop); /* * Now get the room number. It is the next comma separated field, * if there is indeed one. */ if (cp) cp = copy_field (cp, rflg ? (char *) 0:roomno, slop); /* * Now get the work phone number. It is the third field. */ if (cp) cp = copy_field (cp, wflg ? (char *) 0:workph, slop); /* * Now get the home phone number. It is the fourth field. */ if (cp) cp = copy_field (cp, hflg ? (char *) 0:homeph, slop); /* * Anything left over is "slop". */ if (cp && !oflg) { if (slop[0]) strcat (slop, ","); strcat (slop, cp); } /* * If none of the fields were changed from the command line, * let the user interactively change them. */ if (!fflg && !rflg && !wflg && !hflg && !oflg) { printf(_("Changing the user information for %s\n"), user); new_fields(); } /* * Check all of the fields for valid information */ if (valid_field(fullnm, ":,=")) { fprintf(stderr, _("%s: invalid name: \"%s\"\n"), Prog, fullnm); closelog(); exit(1); } if (valid_field(roomno, ":,=")) { fprintf(stderr, _("%s: invalid room number: \"%s\"\n"), Prog, roomno); closelog(); exit(1); } if (valid_field(workph, ":,=")) { fprintf(stderr, _("%s: invalid work phone: \"%s\"\n"), Prog, workph); closelog(); exit(1); } if (valid_field (homeph, ":,=")) { fprintf(stderr, _("%s: invalid home phone: \"%s\"\n"), Prog, homeph); closelog(); exit(1); } if (valid_field(slop, ":")) { fprintf(stderr, _("%s: \"%s\" contains illegal characters\n"), Prog, slop); closelog(); exit(1); } /* * Build the new GECOS field by plastering all the pieces together, * if they will fit ... */ if (strlen(fullnm) + strlen(roomno) + strlen(workph) + strlen(homeph) + strlen(slop) > (unsigned int) 80) { fprintf(stderr, _("%s: fields too long\n"), Prog); closelog(); exit(1); } snprintf(new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s", fullnm, roomno, workph, homeph, slop[0] ? "," : "", slop); /* * Before going any further, raise the ulimit to prevent * colliding into a lowered ulimit, and set the real UID * to root to protect against unexpected signals. Any * keyboard signals are set to be ignored. */ if (setuid(0)) { fprintf(stderr, _("Cannot change ID to root.\n")); SYSLOG((LOG_ERR, NOTROOT2)); closelog(); exit(1); } pwd_init(); /* * The passwd entry is now ready to be committed back to * the password file. Get a lock on the file and open it. */ if (!pw_lock()) { fprintf(stderr, _("Cannot lock the password file; try again later.\n")); SYSLOG((LOG_WARN, PWDBUSY2)); closelog(); exit(1); } if (!pw_open(O_RDWR)) { fprintf(stderr, _("Cannot open the password file.\n")); pw_unlock(); SYSLOG((LOG_ERR, OPNERROR2)); closelog(); exit(1); } /* * Get the entry to update using pw_locate() - we want the real * one from /etc/passwd, not the one from getpwnam() which could * contain the shadow password if (despite the warnings) someone * enables AUTOSHADOW (or SHADOW_COMPAT in libc). --marekm */ pw = pw_locate(user); if (!pw) { pw_unlock(); fprintf(stderr, _("%s: %s not found in /etc/passwd\n"), Prog, user); exit(1); } /* * Make a copy of the entry, then change the gecos field. The other * fields remain unchanged. */ pwent = *pw; pwent.pw_gecos = new_gecos; /* * Update the passwd file entry. If there is a DBM file, * update that entry as well. */ if (!pw_update(&pwent)) { fprintf(stderr, _("Error updating the password entry.\n")); pw_unlock(); SYSLOG((LOG_ERR, UPDERROR2)); closelog(); exit(1); } #ifdef NDBM if (pw_dbm_present() && !pw_dbm_update(&pwent)) { fprintf(stderr, _("Error updating the DBM password entry.\n")); pw_unlock (); SYSLOG((LOG_ERR, DBMERROR2)); closelog(); exit(1); } endpwent(); #endif /* * Changes have all been made, so commit them and unlock the * file. */ if (!pw_close()) { fprintf(stderr, _("Cannot commit password file changes.\n")); pw_unlock(); SYSLOG((LOG_ERR, CLSERROR2)); closelog(); exit(1); } if (!pw_unlock()) { fprintf(stderr, _("Cannot unlock the password file.\n")); SYSLOG((LOG_ERR, UNLKERROR2)); closelog(); exit(1); } SYSLOG((LOG_INFO, CHGGECOS, user)); closelog(); exit (0); }
/* * new_password - validate old password and replace with new (both old and * new in global "char crypt_passwd[128]") */ static int new_password (const struct passwd *pw) { char *clear; /* Pointer to clear text */ char *cipher; /* Pointer to cipher text */ char *cp; /* Pointer to getpass() response */ char orig[200]; /* Original password */ char pass[200]; /* New password */ int i; /* Counter for retries */ int warned; int pass_max_len = -1; char *method; #ifdef HAVE_LIBCRACK_HIST int HistUpdate (const char *, const char *); #endif /* HAVE_LIBCRACK_HIST */ /* * Authenticate the user. The user will be prompted for their own * password. */ if (!amroot && crypt_passwd[0]) { clear = getpass (_("Old password: "******"incorrect password for %s", pw->pw_name)); sleep (1); fprintf (stderr, _("Incorrect password for %s.\n"), pw->pw_name); return -1; } STRFCPY (orig, clear); strzero (clear); strzero (cipher); } else { orig[0] = '\0'; } /* * Get the new password. The user is prompted for the new password * and has five tries to get it right. The password will be tested * for strength, unless it is the root user. This provides an escape * for initial login passwords. */ if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) { if (!getdef_bool ("MD5_CRYPT_ENAB")) { pass_max_len = getdef_num ("PASS_MAX_LEN", 8); } } else { if ( (strcmp (method, "MD5") == 0) #ifdef USE_SHA_CRYPT || (strcmp (method, "SHA256") == 0) || (strcmp (method, "SHA512") == 0) #endif /* USE_SHA_CRYPT */ ) { pass_max_len = -1; } else { pass_max_len = getdef_num ("PASS_MAX_LEN", 8); } } if (!qflg) { if (pass_max_len == -1) { printf (_( "Enter the new password (minimum of %d characters)\n" "Please use a combination of upper and lower case letters and numbers.\n"), getdef_num ("PASS_MIN_LEN", 5)); } else { printf (_( "Enter the new password (minimum of %d, maximum of %d characters)\n" "Please use a combination of upper and lower case letters and numbers.\n"), getdef_num ("PASS_MIN_LEN", 5), pass_max_len); } } warned = 0; for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) { cp = getpass (_("New password: "******"Try again.")); continue; } /* * If enabled, warn about weak passwords even if you are * root (enter this password again to use it anyway). * --marekm */ if (amroot && !warned && getdef_bool ("PASS_ALWAYS_WARN") && (!obscure (orig, pass, pw) || reuse (pass, pw))) { puts (_("\nWarning: weak password (enter it again to use it anyway).")); warned++; continue; } cp = getpass (_("Re-enter new password: "******"They don't match; try again.\n"), stderr); } else { strzero (cp); break; } } memzero (orig, sizeof orig); if (i == 0) { memzero (pass, sizeof pass); return -1; } /* * Encrypt the password, then wipe the cleartext password. */ cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL)); memzero (pass, sizeof pass); #ifdef HAVE_LIBCRACK_HIST HistUpdate (pw->pw_name, crypt_passwd); #endif /* HAVE_LIBCRACK_HIST */ STRFCPY (crypt_passwd, cp); return 0; }
bool spw_file_present (void) { if (getdef_bool ("FORCE_SHADOW")) return true; return commonio_present (&shadow_db); }
int main (int argc, char **argv) { const struct passwd *pw; struct passwd pwent; const struct spwd *spwd; if (1 != argc) { (void) fputs (_("Usage: pwunconv\n"), stderr); } Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); OPENLOG ("pwunconv"); #ifdef WITH_TCB if (getdef_bool("USE_TCB")) { fprintf(stderr, _("%s: can't work with tcb enabled\n"), Prog); exit(1); } #endif /* WITH_TCB */ if (!spw_file_present ()) { /* shadow not installed, do nothing */ exit (0); } if (pw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, pw_dbname ()); fail_exit (5); } pw_locked = true; if (pw_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); fail_exit (1); } if (spw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, spw_dbname ()); fail_exit (5); } spw_locked = true; if (spw_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); fail_exit (1); } pw_rewind (); while ((pw = pw_next ()) != NULL) { spwd = spw_locate (pw->pw_name); if (NULL == spwd) { continue; } pwent = *pw; /* * Update password if non-shadow is "x". */ if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) { pwent.pw_passwd = spwd->sp_pwdp; } /* * Password aging works differently in the two different * systems. With shadow password files you apparently must * have some aging information. The maxweeks or minweeks * may not map exactly. In pwconv we set max == 10000, * which is about 30 years. Here we have to undo that * kludge. So, if maxdays == 10000, no aging information is * put into the new file. Otherwise, the days are converted * to weeks and so on. */ if (pw_update (&pwent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pwent.pw_name); fail_exit (3); } } if (spw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); fail_exit (3); } if (pw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); fail_exit (3); } if (unlink (SHADOW) != 0) { fprintf (stderr, _("%s: cannot delete %s\n"), Prog, SHADOW); SYSLOG ((LOG_ERR, "cannot delete %s", SHADOW)); fail_exit (3); } if (spw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); /* continue */ } if (pw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); /* continue */ } nscd_flush_cache ("passwd"); return 0; }
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; }