📄 pwck.c
字号:
/* Copyright (C) 2004, 2005 Thorsten Kukuk Author: Thorsten Kukuk <kukuk@suse.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#ifdef HAVE_CONFIG_H#include <config.h>#endif#define _GNU_SOURCE#include <time.h>#include <errno.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include <getopt.h>#include <sys/types.h>#include <sys/stat.h>#include "i18n.h"#include "yesno.h"#include "public.h"#include "read-files.h"#define E_SUCCESS 0#define E_USAGE 1#define E_BAD_ENTRY 2#define E_NO_FILE 3#define E_PWDBUSY 4#define E_FAILURE 5#define SCALE DAYchar *files_etc_dir = "/etc";int readonly = 0;static voidprint_usage (FILE *stream, const char *program){ fprintf (stream, _("Usage: %s [-P path] [-q|-r|-s]\n"), program);}static voidprint_help (const char *program){ print_usage (stdout, program); fprintf (stdout, _("%s - check integrity of password files\n\n"), program); fputs (_(" -P path Search passwd, shadow and group file in \"path\"\n"), stdout); fputs (_(" -q, --quiet Don't print warnings, only errors\n"), stdout); fputs (_(" -r, --read-only Run in read-only mode, don't make changes\n"), stdout); fputs (_(" -s, --sort Sort the password file, no checks are done\n"), stdout); fputs (_(" --help Give this help list\n"), stdout); fputs (_(" -u, --usage Give a short usage message\n"), stdout); fputs (_(" -v, --version Print program version\n"), stdout);}static intanswer_yes (void){ if (readonly) { printf (_("No\n")); return 0; } else return yesno ();}#define BLACKLIST_INITIAL_SIZE 512#define BLACKLIST_INCREMENT 256struct blacklist_t{ char *data; int current; int size;};/* returns TRUE if ent->blacklist contains name, else FALSE */static bool_tin_blacklist (const char *name, int namelen, struct blacklist_t *ent){ char buf[namelen + 3]; char *cp; if (ent->data == NULL) return FALSE; buf[0] = '|'; cp = stpcpy (&buf[1], name); *cp++ = '|'; *cp = '\0'; return strstr (ent->data, buf) != NULL;}/* Support routines for remembering login names. The names are stored in a single string with `|' as separator. */static voidblacklist_store_name (const char *name, struct blacklist_t *ent){ int namelen = strlen (name); char *tmp; /* first call, setup cache */ if (ent->size == 0) { ent->size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); ent->data = malloc (ent->size); if (ent->data == NULL) return; ent->data[0] = '|'; ent->data[1] = '\0'; ent->current = 1; } else { if (in_blacklist (name, namelen, ent)) return; /* no duplicates */ if (ent->current + namelen + 1 >= ent->size) { ent->size += MAX (BLACKLIST_INCREMENT, 2 * namelen); tmp = realloc (ent->data, ent->size); if (tmp == NULL) { free (ent->data); ent->size = 0; return; } ent->data = tmp; } } tmp = stpcpy (ent->data + ent->current, name); *tmp++ = '|'; *tmp = '\0'; ent->current += namelen + 1; return;}/* XXX move into the library. */static struct group *files_getgrgid (gid_t gid){ enum nss_status status; static int buflen = 256; static char *buffer = NULL; static struct group resultbuf; if (buffer == NULL) buffer = malloc (buflen); while ((status = files_getgrgid_r (gid, &resultbuf, buffer, buflen, &errno)) == NSS_STATUS_TRYAGAIN && errno == ERANGE) { errno = 0; buflen += 256; buffer = realloc (buffer, buflen); } if (status == NSS_STATUS_SUCCESS) return &resultbuf; else return NULL;}static struct passwd *files_getpwnam (const char *name){ enum nss_status status; static int buflen = 256; static char *buffer = NULL; static struct passwd resultbuf; if (buffer == NULL) buffer = malloc (buflen); while ((status = files_getpwnam_r (name, &resultbuf, buffer, buflen, &errno)) == NSS_STATUS_TRYAGAIN && errno == ERANGE) { errno = 0; buflen += 256; buffer = realloc (buffer, buflen); } if (status == NSS_STATUS_SUCCESS) return &resultbuf; else return NULL;}static intloop_over_passwd_file (int quiet){ struct stat passwd_stat; FILE *input, *output; int output_fd; char *buf = NULL; size_t buflen = 0; struct passwd res; int result = 0; struct blacklist_t blacklist = {NULL, 0, 0}; int modified = 0; char *inputname = alloca (strlen (files_etc_dir) + 8); char *outputname = alloca (strlen (files_etc_dir) + 20); strcpy (inputname, files_etc_dir); strcat (inputname, "/passwd"); strcpy (outputname, files_etc_dir); strcat (outputname, "/passwd.tmpXXXXXX"); if (!quiet) printf (_("Checking `%s'\n"), inputname); input = fopen (inputname, "r"); if (input == NULL) { fprintf (stderr, _("Can't open `%s': %m\n"), inputname); return E_NO_FILE; } if (fstat (fileno (input), &passwd_stat) < 0) { fprintf (stderr, _("Can't stat `%s': %m\n"), inputname); fclose (input); return E_NO_FILE; }#ifdef WITH_SELINUX security_context_t prev_context; if (set_default_context (inputname, &prev_context) < 0) { fclose (input); return E_NO_FILE; }#endif /* Open a temp passwd file */ output_fd = mkstemp (outputname);#ifdef WITH_SELINUX if (restore_default_context (prev_context) < 0) { if (output_fd >= 0) close (output_fd); fclose (input); return E_FAILURE; }#endif if (output_fd == -1) { fprintf (stderr, _("Can't create `%s': %m\n"), inputname); fclose (input); return E_NO_FILE; } if (fchmod (output_fd, passwd_stat.st_mode) < 0) { fprintf (stderr, _("Cannot change permissions for `%s': %s\n"), outputname, strerror (errno)); fclose (input); close (output_fd); unlink (outputname); return E_NO_FILE; } if (fchown (output_fd, passwd_stat.st_uid, passwd_stat.st_gid) < 0) { fprintf (stderr, _("Cannot change owner/group for `%s': %s\n"), outputname, strerror (errno)); fclose (input); close (output_fd); unlink (outputname); return E_NO_FILE; } if (copy_xattr (inputname, outputname) != 0) { fclose (input); close (output_fd); unlink (outputname); return E_NO_FILE; } output = fdopen (output_fd, "w+"); if (output == NULL) { fprintf (stderr, _("Can't open `%s': %m\n"), outputname); fclose (input); close (output_fd); unlink (outputname); return E_NO_FILE; } while (!feof (input)) { char *cp;#if defined(HAVE_GETLINE) ssize_t n = getline (&buf, &buflen, input);#elif defined (HAVE_GETDELIM) ssize_t n = getdelim (&buf, &buflen, '\n', input);#else ssize_t n; if (buf == NULL) { buflen = 8096; buf = malloc (buflen); } buf[0] = '\0'; fgets (buf, buflen - 1, input); if (buf != NULL) n = strlen (buf); else n = 0;#endif /* HAVE_GETLINE / HAVE_GETDELIM */ if (buf) cp = strdup (buf); else cp = strdup (""); if (n < 1) { if (feof (input)) continue; result = E_BAD_ENTRY; printf (_("Invalid password entry.\n")); printf (_("Delete empty line? ")); if (answer_yes ()) { free (cp); modified = 1; continue; } else goto write_pw; } /* Remove trailing '\n'. */ if (cp[strlen (cp) - 1] == '\n') cp[strlen (cp) - 1] = '\0'; if (*cp == '+' || *cp == '-') goto write_pw; /* Comments are not allowed in /etc/passwd. */ if (strchr (cp, '#') != NULL) { result = E_BAD_ENTRY; printf (_("Invalid password entry with comment.\n")); printf (_("Delete line `%s'? "), cp); if (answer_yes ()) { modified = 1; free (cp); continue; } else goto write_pw; } /* Parse string in strict mode and report error. */ if (parse_pwent (buf, &res, 1) != 1) { result = E_BAD_ENTRY; printf (_("Invalid password entry.\n")); printf (_("Delete line `%s'? "), cp); if (answer_yes ()) { modified = 1; free (cp); continue; } else goto write_pw; } /* Check for invalid characters in username. */ if (check_name (res.pw_name) < 0) { result = E_BAD_ENTRY; printf (_("Invalid account name `%s'.\n"), res.pw_name); printf (_("Delete line `%s'? "), cp); if (answer_yes ()) { modified = 1; free (cp); continue; } else goto write_pw; } /* Check, if we saw this user name already. */ if (in_blacklist (res.pw_name, strlen (res.pw_name), &blacklist)) { result = E_BAD_ENTRY; printf (_("Duplicate password entry\n")); printf (_("Delete line `%s'? "), cp); if (answer_yes ()) { modified = 1; free (cp); continue; } else goto write_pw; } /* Mark the username as seen, but after checking for duplicate! */ blacklist_store_name (res.pw_name, &blacklist); /* Check, if primary group exists. */ if (!quiet && getgrgid (res.pw_gid) == NULL && files_getgrgid (res.pw_gid) == NULL) { result = E_BAD_ENTRY; printf (_("User `%s': unknown group `%u'\n"), res.pw_name, res.pw_gid); } /* Check, if the home directory exists. */ if (!quiet && access (res.pw_dir, F_OK)) { result = E_BAD_ENTRY; printf (_("User `%s': directory `%s' does not exist.\n"), res.pw_name, res.pw_dir); } /* Check, if shell is executable. */ if (!quiet && res.pw_shell[0] && access (res.pw_shell, X_OK)) { result = E_BAD_ENTRY; printf (_("User `%s': shell `%s' is not executeable.\n"), res.pw_name, res.pw_shell); } write_pw: fprintf (output, "%s\n", cp); free (cp); } fclose (input); fclose (output); if (modified) { char *oldname = alloca (strlen (files_etc_dir) + 20); strcpy (oldname, files_etc_dir); strcat (oldname, "/passwd.old"); unlink (oldname); if (link (inputname, oldname) < 0) fprintf (stderr, _("Warning: cannot create backup file `%s': %m\n"), oldname); rename (outputname, inputname); } else unlink (outputname); return result;}static intloop_over_shadow_file (int quiet){ struct stat shadow_stat; FILE *input, *output; int output_fd; char *buf = NULL; size_t buflen = 0; struct spwd res; int result = 0; struct blacklist_t blacklist = {NULL, 0, 0}; int modified = 0; char *inputname = alloca (strlen (files_etc_dir) + 8); char *outputname = alloca (strlen (files_etc_dir) + 20); strcpy (inputname, files_etc_dir); strcat (inputname, "/shadow"); strcpy (outputname, files_etc_dir); strcat (outputname, "/shadow.tmpXXXXXX"); input = fopen (inputname, "r"); if (input == NULL) { /* fprintf (stderr, _("Can't open `%s': %m\n"), inputname); */ return E_NO_FILE; } if (!quiet) printf (_("Checking `%s'.\n"), inputname); if (fstat (fileno (input), &shadow_stat) < 0) { fprintf (stderr, _("Can't stat `%s': %m\n"), inputname); fclose (input); return E_NO_FILE; }#ifdef WITH_SELINUX security_context_t prev_context; if (set_default_context (inputname, &prev_context) < 0) { fclose (input); return E_NO_FILE; }#endif /* Open a temp shadow file */ output_fd = mkstemp (outputname);#ifdef WITH_SELINUX if (restore_default_context (prev_context) < 0) { if (output_fd >= 0) close (output_fd); fclose (input); return E_FAILURE; }#endif if (output_fd == -1) { fprintf (stderr, _("Can't create `%s': %m\n"), inputname); fclose (input); return E_NO_FILE; } if (fchmod (output_fd, shadow_stat.st_mode) < 0) { fprintf (stderr, _("Cannot change permissions for `%s': %s\n"), outputname, strerror (errno)); fclose (input); close (output_fd); unlink (outputname); return E_NO_FILE; } if (fchown (output_fd, shadow_stat.st_uid, shadow_stat.st_gid) < 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -