From owner-freebsd-current@FreeBSD.ORG Wed Feb 20 12:28:30 2013 Return-Path: Delivered-To: freebsd-current@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id 5C1BB6EC for ; Wed, 20 Feb 2013 12:28:30 +0000 (UTC) (envelope-from simon@comsys.ntu-kpi.kiev.ua) Received: from comsys.kpi.ua (comsys.kpi.ua [77.47.192.42]) by mx1.freebsd.org (Postfix) with ESMTP id 16AAADF9 for ; Wed, 20 Feb 2013 12:28:29 +0000 (UTC) Received: from pm513-1.comsys.kpi.ua ([10.18.52.101] helo=pm513-1.comsys.ntu-kpi.kiev.ua) by comsys.kpi.ua with esmtpsa (TLSv1:AES256-SHA:256) (Exim 4.63) (envelope-from ) id 1U88mg-0004JQ-2U; Wed, 20 Feb 2013 14:28:26 +0200 Received: by pm513-1.comsys.ntu-kpi.kiev.ua (Postfix, from userid 1001) id 8785F1E08A; Wed, 20 Feb 2013 14:28:26 +0200 (EET) Date: Wed, 20 Feb 2013 14:28:26 +0200 From: Andrey Simonenko To: Rick Macklem Subject: Re: Possible bug in NFSv4 with krb5p security? Message-ID: <20130220122826.GA13613@pm513-1.comsys.ntu-kpi.kiev.ua> References: <20130219102744.GA2426@pm513-1.comsys.ntu-kpi.kiev.ua> <747768617.3137250.1361325169865.JavaMail.root@erie.cs.uoguelph.ca> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <747768617.3137250.1361325169865.JavaMail.root@erie.cs.uoguelph.ca> User-Agent: Mutt/1.5.21 (2010-09-15) X-Authenticated-User: simon@comsys.ntu-kpi.kiev.ua X-Authenticator: plain X-Sender-Verify: SUCCEEDED (sender exists & accepts mail) X-Exim-Version: 4.63 (build at 28-Apr-2011 07:11:12) X-Date: 2013-02-20 14:28:26 X-Connected-IP: 10.18.52.101:49381 X-Message-Linecount: 170 X-Body-Linecount: 153 X-Message-Size: 5301 X-Body-Size: 4473 Cc: freebsd-current@freebsd.org, Elias Martenson , Benjamin Kaduk X-BeenThere: freebsd-current@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Discussions about the use of FreeBSD-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Feb 2013 12:28:30 -0000 On Tue, Feb 19, 2013 at 08:52:49PM -0500, Rick Macklem wrote: > > > > I cannot find how to get information about maximum buffer size for > > the getpwnam_r() function. This information should be returned by > > sysconf(_SC_GETPW_R_SIZE_MAX), but since it does not work on FreeBSD > > it is necessary to guess its size. Original value is 128 and it works > > for somebody, 1024 works for your environment, but it can fail for > > another environment. > > > > SUSv4 specifies "Storage referenced by the structure is allocated from > > the memory provided with the buffer parameter", but then tells about > > groups > > in EXAMPLE for getpwnam_r() "Note that sysconf(_SC_GETPW_R_SIZE_MAX) > > may > > return -1 if there is no hard limit on the size of the buffer needed > > to > > store all the groups returned". > > > > malloc() can give overhead, but that function can try to call > > getpwnam_r() > > with buffer allocated from stack and if getpwnam_r() failed with > > ERANGE > > use dynamically allocated buffer. > > > > #define PWBUF_SIZE_INI (2 * MAXLOGNAME + 2 * MAXPATHLEN + > > _PASSWORD_LEN + 1) > > #define PWBUF_SIZE_INC 128 > > > > char bufs[2 * MAXLOGNAME + MAXPATHLEN + PASSWORD_LEN + 1 + 32]; > > > > error = getpwnam_r(lname, &pwd, bufs, sizeof(bufs), &pw); > > if (pw != NULL) { > > *uidp = pw->pw_uid; > > return (GSS_S_COMPLETE); > > } else if (error != ERANGE) > > return (GSS_S_FAILURE); > > > > size = PWBUF_SIZE_INI; > > for (;;) { > > size += PWBUF_SIZE_INC; > > buf = malloc(size); > > if (buf == NULL) > > return (GSS_S_FAILURE); > > error = getpwnam_r(lname, &pwd, buf, size, &pw); > > free(buf); > > if (pw != NULL) { > > *uidp = pw->pw_uid; > > return (GSS_S_COMPLETE); > > } else { > > if (error == ERANGE && > > size <= SIZE_MAX - PWBUF_SIZE_INC) > > continue; > > return (GSS_S_FAILURE); > > } > > } > > Just my opinion, but I think the above is a good approach. > (ie. First trying a fairly large buffer on the stack that > will succeed 99.99% of the time, but check for ERANGE and > loop trying progressively larger malloc'd buffers until > it stops reporting ERANGE.) > > I suspect the overheads behind getpwnam_r() are larger than > the difference between using a buffer on the stack vs malloc, > so I think it should use a fairly large buffer the first time. > > Personally, I might have coded it as a single do { } while(), > with the first attempt in it, but that's just personal stylistic > taste. (You can check for buf != bufs before doing a free() of it.) > And, if you wanted to be clever, the code could use a static "bufsiz_hint", > which is set to the largest size needed sofar and that is used as > the initial malloc size. That way it wouldn't loop as much for a > site with huge passwd entries. (An entire bio in the GECOS field or ???) > Thanks for the review. Another variant. This is a program that can be used for verifying correctness of the function, just change PWBUF_SIZE_* values and put some printfs to see when buffer is reallocated. sizehint is updated only when malloc() succeeded. --------------------------------- #include #include #include #include #include #include #include #include #include #include static int getpwnam_r_func(const char *name, uid_t *uidp) { #define PWBUF_SIZE_INI (2 * MAXLOGNAME + MAXPATHLEN + _PASSWORD_LEN) #define PWBUF_SIZE_INC 128 static size_t sizehint = PWBUF_SIZE_INI; struct passwd pwd; struct passwd *pw; char *buf; size_t size; int error; char lname[MAXLOGNAME]; char bufs[PWBUF_SIZE_INI]; strncpy(lname, name, sizeof(lname)); buf = bufs; size = sizeof(bufs); for (;;) { error = getpwnam_r(lname, &pwd, buf, size, &pw); if (buf != bufs) free(buf); if (pw != NULL) { *uidp = pw->pw_uid; return (GSS_S_COMPLETE); } else if (error != ERANGE || size > SIZE_MAX - PWBUF_SIZE_INC) return (GSS_S_FAILURE); if (size != sizehint) size = sizehint; else size += PWBUF_SIZE_INC; buf = malloc(size); if (buf == NULL) return (GSS_S_FAILURE); sizehint = size; } } int main(void) { const char *str[] = { "man", "root", "q", "bin", NULL }; uid_t uid; u_int i; for (i = 0; str[i] != NULL; ++i) { printf("%-20s\t", str[i]); if (getpwnam_r_func(str[i], &uid) == GSS_S_COMPLETE) printf("%u\n", uid); else printf("not found\n"); } return (0); } ---------------------------------