Subject: lib/18150: "passwd: /etc/master.passwd: entry inconsistent" not helpful
To: None <gnats-bugs@gnats.netbsd.org>
From: John F. Woods <jfw@jfwhome.funhouse.com>
List: netbsd-bugs
Date: 09/02/2002 12:27:12
>Number:         18150
>Category:       lib
>Synopsis:       "passwd: /etc/master.passwd: entry inconsistent" not helpful
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Sep 02 09:28:00 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     John F. Woods
>Release:        NetBSD 1.6F
>Organization:
Misanthropes-R-Us
>Environment:
	
	
System: NetBSD jfwhome.funhouse.com 1.6F NetBSD 1.6F (JFW) #6: Sun Aug 18 12:28:13 EDT 2002 jfw@jfwhome.funhouse.com:/usr/src/sys/arch/i386/compile/JFW i386
Architecture: i386
Machine: i386
>Description:
Attempting to change a user's passwd, I was rewarded with the unhelpful
error messages
    passwd: /etc/master.passwd: entry inconsistent
    passwd: /etc/master.passwd: unchanged
This does not tell which entry is inconsistent, nor does it tell what is
consistent.  After inserting some debug code (cleaned up and offered as a
fix below), I discovered that I had apparently sometime changed the group ID
of the entry I wanted to change without having updated the pwd database.

It would be much better if passwd could issue an error message indicating
which entry and what field was inconsistent, and I offer the code to do so
below.

>How-To-Repeat:
1. Edit master.passwd manually to change a gid for some user.
2. Run "passwd" to change that user's passwd.
3. Pretend you did step 1 so long ago that you do not remember it, and
figure out what is wrong.

>Fix:
The following results in a somewhat more useful error message.

*** passwd.c.orig	Mon Sep  2 09:55:50 2002
--- passwd.c	Mon Sep  2 12:10:17 2002
***************
*** 62,68 ****
  
  static const char      *pw_filename(const char *filename);
  static void		pw_cont(int sig);
! static int		pw_equal(char *buf, struct passwd *old_pw);
  static const char      *pw_default(const char *option);
  static int		read_line(FILE *fp, char *line, int max);
  static void		trim_whitespace(char *line);
--- 62,68 ----
  
  static const char      *pw_filename(const char *filename);
  static void		pw_cont(int sig);
! static const char *	pw_equal(char *buf, struct passwd *old_pw);
  static const char      *pw_default(const char *option);
  static int		read_line(FILE *fp, char *line, int max);
  static void		trim_whitespace(char *line);
***************
*** 296,302 ****
  }
  
  /* for use in pw_copy(). Compare a pw entry to a pw struct. */
! static int
  pw_equal(char *buf, struct passwd *pw)
  {
  	struct passwd buf_pw;
--- 296,303 ----
  }
  
  /* for use in pw_copy(). Compare a pw entry to a pw struct. */
! /* returns a character string labelling the miscompared field or 0 */
! static const char *
  pw_equal(char *buf, struct passwd *pw)
  {
  	struct passwd buf_pw;
***************
*** 309,324 ****
  	if (buf[len-1] == '\n')
  		buf[len-1] = '\0';
  	if (!pw_scan(buf, &buf_pw, NULL))
! 		return 0;
! 	return !strcmp(pw->pw_name, buf_pw.pw_name)
! 		&& pw->pw_uid == buf_pw.pw_uid
! 		&& pw->pw_gid == buf_pw.pw_gid
! 		&& !strcmp(pw->pw_class, buf_pw.pw_class)
! 		&& (long)pw->pw_change == (long)buf_pw.pw_change
! 		&& (long)pw->pw_expire == (long)buf_pw.pw_expire
! 		&& !strcmp(pw->pw_gecos, buf_pw.pw_gecos)
! 		&& !strcmp(pw->pw_dir, buf_pw.pw_dir)
! 		&& !strcmp(pw->pw_shell, buf_pw.pw_shell);
  }
  
  void
--- 310,335 ----
  	if (buf[len-1] == '\n')
  		buf[len-1] = '\0';
  	if (!pw_scan(buf, &buf_pw, NULL))
! 		return "corrupt line";
! 	if (strcmp(pw->pw_name, buf_pw.pw_name) != 0)
! 		return "name";
! 	if (pw->pw_uid != buf_pw.pw_uid)
! 		return "uid";
! 	if (pw->pw_gid != buf_pw.pw_gid)
! 		return "gid";
!         if (strcmp( pw->pw_class, buf_pw.pw_class) != 0)
! 		return "class";
! 	if (pw->pw_change != buf_pw.pw_change)
! 		return "change";
! 	if (pw->pw_expire != buf_pw.pw_expire)
! 		return "expire";
!         if (strcmp( pw->pw_gecos, buf_pw.pw_gecos) != 0)
! 		return "gecos";
!         if (strcmp( pw->pw_dir, buf_pw.pw_dir) != 0)
! 		return "dir";
!         if (strcmp( pw->pw_shell, buf_pw.pw_shell) != 0)
! 		return "shell";
! 	return (char *)0;
  }
  
  void
***************
*** 345,350 ****
--- 356,362 ----
  		pw_error(mpwdl, 1, 1);
  
  	for (done = 0; fgets(buf, sizeof(buf), from);) {
+ 		const char *neq;
  		if (!strchr(buf, '\n')) {
  			warnx("%s: line too long", mpwd);
  			pw_error(NULL, 0, 1);
***************
*** 368,375 ****
  			continue;
  		}
  		*p = ':';
! 		if (old_pw && !pw_equal(buf, old_pw)) {
! 			warnx("%s: entry inconsistent", mpwd);
  			pw_error(NULL, 0, 1);
  		}
  		(void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
--- 380,392 ----
  			continue;
  		}
  		*p = ':';
! 		if (old_pw && (neq = pw_equal(buf, old_pw))) {
! 			if (strcmp(neq,"corrupt line")==0)
! 				warnx("%s: entry %s corrupted", mpwd,
! 				      pw->pw_name);
! 			else
! 				warnx("%s: entry %s inconsistent %s",
! 				      mpwd, pw->pw_name, neq);
  			pw_error(NULL, 0, 1);
  		}
  		(void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",

>Release-Note:
>Audit-Trail:
>Unformatted: