From nobody@FreeBSD.org  Thu Mar  6 05:40:54 2014
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1])
	(using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))
	(No client certificate requested)
	by hub.freebsd.org (Postfix) with ESMTPS id F2C53E38
	for <freebsd-gnats-submit@FreeBSD.org>; Thu,  6 Mar 2014 05:40:54 +0000 (UTC)
Received: from cgiserv.freebsd.org (cgiserv.freebsd.org [IPv6:2001:1900:2254:206a::50:4])
	(using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
	(No client certificate requested)
	by mx1.freebsd.org (Postfix) with ESMTPS id D3280E41
	for <freebsd-gnats-submit@FreeBSD.org>; Thu,  6 Mar 2014 05:40:54 +0000 (UTC)
Received: from cgiserv.freebsd.org ([127.0.1.6])
	by cgiserv.freebsd.org (8.14.8/8.14.8) with ESMTP id s265esL6049304
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 6 Mar 2014 05:40:54 GMT
	(envelope-from nobody@cgiserv.freebsd.org)
Received: (from nobody@localhost)
	by cgiserv.freebsd.org (8.14.8/8.14.8/Submit) id s265esIg049297;
	Thu, 6 Mar 2014 05:40:54 GMT
	(envelope-from nobody)
Message-Id: <201403060540.s265esIg049297@cgiserv.freebsd.org>
Date: Thu, 6 Mar 2014 05:40:54 GMT
From: Kim Shrier <fbsdbugs@westryn.net>
To: freebsd-gnats-submit@FreeBSD.org
Subject: [patch] pw command segfaults when the -V parameter is used on commands that alter groups
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         187310
>Category:       bin
>Synopsis:       [patch] pw command segfaults when the -V parameter is used on commands that alter groups
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    julian
>State:          patched
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Mar 06 05:50:00 UTC 2014
>Closed-Date:    
>Last-Modified:  Thu Mar  6 21:50:02 UTC 2014
>Originator:     Kim Shrier
>Release:        10.0 Release amd64
>Organization:
>Environment:
FreeBSD snorri.lab.westryn.net 10.0-RELEASE FreeBSD 10.0-RELEASE #1 r261308M: Sun Feb 23 18:45:19 MST 2014     carol@snorri.lab.westryn.net:/usr/obj/usr/src/sys/SNORRI_02  amd64
>Description:
When specifying an alternate location of the etc directory using the -V command line parameter to /usr/sbin/pw, the pw command segfaults when it does anything that updates groups
>How-To-Repeat:
Create a master.passwd and group file in a directory other than /etc.  For the sake of this description, I'll use /tmp/pw_problem/etc.

mkdir -p /tmp/pw_problem/etc
cd /tmp/pw_problem/etc

Create a master.passwd file in this directory that contains a line like:

bob:*:1001:1001::0:0:Robert:/home/bob:/bin/sh

Create a group file in this directory that contains a line like:

bob:*:1001:

Run pwd_mkdb.

pwd_mkdb -p -d /tmp/pw_problem/etc master.passwd

Now, try to delete the user with pw

pw -V /tmp/pw_problem/etc userdel bob
Segmentation fault (core dumped)

>Fix:
The problem is that the gr_mem member of the group struct is dereferenced without first checking to see if it is NULL.  This occurs in both /usr/src/usr.sbin/pw/pw_group.c and /usr/src/usr.sbin/pw/pw_user.c.

The reason this happens only when the -V parameter is used is because pw uses different routines based on whether or not the -V parameter is present.  When it isn't specified, it uses getgrent, getgrgid, and getgrnam from libc.  When -V is specified, it uses vgetgrent, vgetgrgid, and vgetgrnam which uses code from pw_vpw.c which is part of the source for pw.  These three routines call vnextgrent which eventually calls gr_scan from libutil.  Looking at the source in libutil, it is possible for the group structure returned by gr_scan to have a NULL gr_mem.  Other code in libutil deals with this possibility.  The pw code does not.

I am attaching a patch file that I made against head.

The rcsid for pw_group is:
static const char rcsid[] =
  "$FreeBSD: head/usr.sbin/pw/pw_group.c 244738 2012-12-27 14:44:13Z bapt $";

The rcsid for pw_user is:
static const char rcsid[] =
  "$FreeBSD: head/usr.sbin/pw/pw_user.c 252688 2013-07-04 07:59:11Z des $";


Patch attached with submission follows:

--- usr.sbin/pw/pw_group.c.orig	2014-03-05 21:12:10.000000000 -0700
+++ usr.sbin/pw/pw_group.c	2014-03-05 21:22:03.000000000 -0700
@@ -227,10 +227,12 @@
 		else if (arg->ch == 'm') {
 			int	k = 0;
 
-			while (grp->gr_mem[k] != NULL) {
-				if (extendarray(&members, &grmembers, i + 2) != -1)
-					members[i++] = grp->gr_mem[k];
-				k++;
+			if (grp->gr_mem != NULL) {
+				while (grp->gr_mem[k] != NULL) {
+					if (extendarray(&members, &grmembers, i + 2) != -1)
+						members[i++] = grp->gr_mem[k];
+					k++;
+				}
 			}
 		}
 
@@ -311,6 +313,9 @@
 	int k;
 	struct passwd *pwd;
 
+	if (grp->gr_mem == NULL)
+		return;
+
 	k = 0;
 	while (grp->gr_mem[k] != NULL) {
 		matchFound = false;
@@ -415,8 +420,10 @@
 		printf("Group Name: %-15s   #%lu\n"
 		       "   Members: ",
 		       grp->gr_name, (long) grp->gr_gid);
-		for (i = 0; grp->gr_mem[i]; i++)
-			printf("%s%s", i ? "," : "", grp->gr_mem[i]);
+		if (grp->gr_mem != NULL) {
+			for (i = 0; grp->gr_mem[i]; i++)
+				printf("%s%s", i ? "," : "", grp->gr_mem[i]);
+		}
 		fputs("\n\n", stdout);
 	}
 	return EXIT_SUCCESS;
--- usr.sbin/pw/pw_user.c.orig	2014-03-05 21:12:10.000000000 -0700
+++ usr.sbin/pw/pw_user.c	2014-03-05 21:21:43.000000000 -0700
@@ -425,19 +425,21 @@
 			}
 
 			grp = GETGRNAM(a_name->val);
-			if (grp != NULL && *grp->gr_mem == NULL)
+			if (grp != NULL && (grp->gr_mem == NULL || *grp->gr_mem == NULL))
 				delgrent(GETGRNAM(a_name->val));
 			SETGRENT();
 			while ((grp = GETGRENT()) != NULL) {
 				int i;
 				char group[MAXLOGNAME];
-				for (i = 0; grp->gr_mem[i] != NULL; i++) {
-					if (!strcmp(grp->gr_mem[i], a_name->val)) {
-						while (grp->gr_mem[i] != NULL) {
-							grp->gr_mem[i] = grp->gr_mem[i+1];
-						}	
-						strlcpy(group, grp->gr_name, MAXLOGNAME);
-						chggrent(group, grp);
+				if (grp->gr_mem != NULL) {
+					for (i = 0; grp->gr_mem[i] != NULL; i++) {
+						if (!strcmp(grp->gr_mem[i], a_name->val)) {
+							while (grp->gr_mem[i] != NULL) {
+								grp->gr_mem[i] = grp->gr_mem[i+1];
+							}	
+							strlcpy(group, grp->gr_name, MAXLOGNAME);
+							chggrent(group, grp);
+						}
 					}
 				}
 			}
@@ -908,7 +910,7 @@
 				errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
 		}
 		gid = grp->gr_gid;
-	} else if ((grp = GETGRNAM(nam)) != NULL && grp->gr_mem[0] == NULL) {
+	} else if ((grp = GETGRNAM(nam)) != NULL && (grp->gr_mem == NULL || grp->gr_mem[0] == NULL)) {
 		gid = grp->gr_gid;  /* Already created? Use it anyway... */
 	} else {
 		struct cargs    grpargs;
@@ -1182,14 +1184,17 @@
 		while ((grp=GETGRENT()) != NULL)
 		{
 			int     i = 0;
-			while (grp->gr_mem[i] != NULL)
+			if (grp->gr_mem != NULL)
 			{
-				if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
+				while (grp->gr_mem[i] != NULL)
 				{
-					printf(j++ == 0 ? "    Groups: %s" : ",%s", grp->gr_name);
-					break;
+					if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
+					{
+						printf(j++ == 0 ? "    Groups: %s" : ",%s", grp->gr_name);
+						break;
+					}
+					++i;
 				}
-				++i;
 			}
 		}
 		ENDGRENT();


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->julian 
Responsible-Changed-By: julian 
Responsible-Changed-When: Thu Mar 6 11:27:35 PST 2014 
Responsible-Changed-Why:  
Take this one.  patch committed to -current revision 262864. 
Will MFC to 10,9,8 in a week 

http://www.freebsd.org/cgi/query-pr.cgi?pr=187310 
State-Changed-From-To: open->patched 
State-Changed-By: julian 
State-Changed-When: Thu Mar 6 11:58:45 PST 2014 
State-Changed-Why:  
part 2 committed as revision 262865. MFC to come 

http://www.freebsd.org/cgi/query-pr.cgi?pr=187310 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/187310: commit references a PR
Date: Thu,  6 Mar 2014 21:41:43 +0000 (UTC)

 Author: julian
 Date: Thu Mar  6 19:58:03 2014
 New Revision: 262865
 URL: http://svnweb.freebsd.org/changeset/base/262865
 
 Log:
   Part 2 of bug 187310.. had to commit separately due to local confusion.
   Don't let pw crash when give certain input.
   
   PR:		187310
   Submitted by:	Kim Shrier
   MFC after:	1 week
 
 Modified:
   head/usr.sbin/pw/pw_user.c
 
 Modified: head/usr.sbin/pw/pw_user.c
 ==============================================================================
 --- head/usr.sbin/pw/pw_user.c	Thu Mar  6 19:26:08 2014	(r262864)
 +++ head/usr.sbin/pw/pw_user.c	Thu Mar  6 19:58:03 2014	(r262865)
 @@ -425,19 +425,22 @@ pw_user(struct userconf * cnf, int mode,
  			}
  
  			grp = GETGRNAM(a_name->val);
 -			if (grp != NULL && *grp->gr_mem == NULL)
 +			if (grp != NULL &&
 +			    (grp->gr_mem == NULL || *grp->gr_mem == NULL))
  				delgrent(GETGRNAM(a_name->val));
  			SETGRENT();
  			while ((grp = GETGRENT()) != NULL) {
  				int i;
  				char group[MAXLOGNAME];
 -				for (i = 0; grp->gr_mem[i] != NULL; i++) {
 -					if (!strcmp(grp->gr_mem[i], a_name->val)) {
 -						while (grp->gr_mem[i] != NULL) {
 -							grp->gr_mem[i] = grp->gr_mem[i+1];
 -						}	
 -						strlcpy(group, grp->gr_name, MAXLOGNAME);
 -						chggrent(group, grp);
 +				if (grp->gr_mem != NULL) {
 +					for (i = 0; grp->gr_mem[i] != NULL; i++) {
 +						if (!strcmp(grp->gr_mem[i], a_name->val)) {
 +							while (grp->gr_mem[i] != NULL) {
 +								grp->gr_mem[i] = grp->gr_mem[i+1];
 +							}	
 +							strlcpy(group, grp->gr_name, MAXLOGNAME);
 +							chggrent(group, grp);
 +						}
  					}
  				}
  			}
 @@ -908,7 +911,8 @@ pw_gidpolicy(struct userconf * cnf, stru
  				errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
  		}
  		gid = grp->gr_gid;
 -	} else if ((grp = GETGRNAM(nam)) != NULL && grp->gr_mem[0] == NULL) {
 +	} else if ((grp = GETGRNAM(nam)) != NULL &&
 +	    (grp->gr_mem == NULL || grp->gr_mem[0] == NULL)) {
  		gid = grp->gr_gid;  /* Already created? Use it anyway... */
  	} else {
  		struct cargs    grpargs;
 @@ -1182,14 +1186,16 @@ print_user(struct passwd * pwd, int pret
  		while ((grp=GETGRENT()) != NULL)
  		{
  			int     i = 0;
 -			while (grp->gr_mem[i] != NULL)
 -			{
 -				if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
 +			if (grp->gr_mem != NULL) {
 +				while (grp->gr_mem[i] != NULL)
  				{
 -					printf(j++ == 0 ? "    Groups: %s" : ",%s", grp->gr_name);
 -					break;
 +					if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
 +					{
 +						printf(j++ == 0 ? "    Groups: %s" : ",%s", grp->gr_name);
 +						break;
 +					}
 +					++i;
  				}
 -				++i;
  			}
  		}
  		ENDGRENT();
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/187310: commit references a PR
Date: Thu,  6 Mar 2014 21:43:22 +0000 (UTC)

 Author: julian
 Date: Thu Mar  6 19:26:08 2014
 New Revision: 262864
 URL: http://svnweb.freebsd.org/changeset/base/262864
 
 Log:
   Stop pw(8) from segfaulting when given certain input
   
   PR:187310
   Submitted by:	Kim Shrier
   Obtained from:	bug
   MFC after:	1 week
 
 Modified:
   head/usr.sbin/pw/pw_group.c
 
 Modified: head/usr.sbin/pw/pw_group.c
 ==============================================================================
 --- head/usr.sbin/pw/pw_group.c	Thu Mar  6 18:50:35 2014	(r262863)
 +++ head/usr.sbin/pw/pw_group.c	Thu Mar  6 19:26:08 2014	(r262864)
 @@ -227,10 +227,12 @@ pw_group(struct userconf * cnf, int mode
  		else if (arg->ch == 'm') {
  			int	k = 0;
  
 -			while (grp->gr_mem[k] != NULL) {
 -				if (extendarray(&members, &grmembers, i + 2) != -1)
 -					members[i++] = grp->gr_mem[k];
 -				k++;
 +			if (grp->gr_mem != NULL) {
 +				while (grp->gr_mem[k] != NULL) {
 +					if (extendarray(&members, &grmembers, i + 2) != -1)
 +						members[i++] = grp->gr_mem[k];
 +					k++;
 +				}
  			}
  		}
  
 @@ -311,6 +313,9 @@ delete_members(char ***members, int *grm
  	int k;
  	struct passwd *pwd;
  
 +	if (grp->gr_mem == NULL)
 +		return;
 +
  	k = 0;
  	while (grp->gr_mem[k] != NULL) {
  		matchFound = false;
 @@ -415,8 +420,10 @@ print_group(struct group * grp, int pret
  		printf("Group Name: %-15s   #%lu\n"
  		       "   Members: ",
  		       grp->gr_name, (long) grp->gr_gid);
 -		for (i = 0; grp->gr_mem[i]; i++)
 -			printf("%s%s", i ? "," : "", grp->gr_mem[i]);
 +		if (grp->gr_mem != NULL) {
 +			for (i = 0; grp->gr_mem[i]; i++)
 +				printf("%s%s", i ? "," : "", grp->gr_mem[i]);
 +		}
  		fputs("\n\n", stdout);
  	}
  	return EXIT_SUCCESS;
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
>Unformatted:
