>Number:         170295
>Category:       bin
>Synopsis:       mountd: correct credentials parsing in -mapall and -maproot 
>options
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Tue Jul 31 13:00:25 UTC 2012
>Closed-Date:
>Last-Modified:
>Originator:     Andrey Simonenko
>Release:        FreeBSD 10.0-CURRENT amd64
>Organization:
>Environment:
>Description:

The usr.sbin/mountd/mountd.c:parsecred() function has the following mistakes:

1. It has buffer overflow if number of GIDs of some user is greater than
   the XU_NGROUPS value, incorrect usage of getgrouplist(3).

2. It incorrectly gets group lists for a user given without groups: it
   forgets about a single group of a user or forgets about the first
   supplementary group of a user.

3. If a user is unknown it silently uses -2:-2 credentials and this
   does not correspond to exports(5) rules.

4. If a group is unknown, then it ignores this group and this
   does not correspond to exports(5) rules.

5. It uses atoi(3) function to parse UID and GID, and does not check
   any mistakes in numbers.

>How-To-Repeat:
>Fix:
--- mountd.c.orig       2012-01-20 13:19:39.000000000 +0200
+++ mountd.c    2012-07-31 15:31:39.000000000 +0300
@@ -199,7 +199,7 @@ int makemask(struct sockaddr_storage *ss
 void   mntsrv(struct svc_req *, SVCXPRT *);
 void   nextfield(char **, char **);
 void   out_of_mem(void);
-void   parsecred(char *, struct xucred *);
+static int     parsecred(char *, struct xucred *);
 int    parsesec(char *, struct exportlist *);
 int    put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
 void   *sa_rawaddr(struct sockaddr *sa, int *nbytes);
@@ -2140,7 +2140,8 @@ do_opt(char **cpp, char **endcpp, struct
                    !(allflag = strcmp(cpopt, "mapall")) ||
                    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
                        usedarg++;
-                       parsecred(cpoptarg, cr);
+                       if (parsecred(cpoptarg, cr))
+                               return (1);
                        if (allflag == 0) {
                                *exflagsp |= MNT_EXPORTANON;
                                opt_flags |= OP_MAPALL;
@@ -2760,81 +2761,100 @@ get_line(void)
 /*
  * Parse a description of a credential.
  */
-void
+static int
 parsecred(char *namelist, struct xucred *cr)
 {
-       char *name;
-       int cnt;
-       char *names;
-       struct passwd *pw;
-       struct group *gr;
-       gid_t groups[XU_NGROUPS + 1];
+       const struct group *gr;
+       const struct passwd *pw;
+       const char *errstr, *username;
+       char *name, *names;
+       uid_t uid;
        int ngroups;
 
-       cr->cr_version = XUCRED_VERSION;
-       /*
-        * Set up the unprivileged user.
-        */
-       cr->cr_uid = -2;
-       cr->cr_groups[0] = -2;
-       cr->cr_ngroups = 1;
        /*
         * Get the user's password table entry.
         */
        names = strsep(&namelist, " \t\n");
-       name = strsep(&names, ":");
-       if (isdigit(*name) || *name == '-')
-               pw = getpwuid(atoi(name));
-       else
-               pw = getpwnam(name);
-       /*
-        * Credentials specified as those of a user.
-        */
-       if (names == NULL) {
-               if (pw == NULL) {
-                       syslog(LOG_ERR, "unknown user: %s", name);
-                       return;
+       username = name = strsep(&names, ":");
+       errno = 0;
+       pw = getpwnam(name);
+       if (pw == NULL) {
+               if (errno != 0) {
+                       syslog(LOG_ERR, "getpwnam: %m");
+                       return (1);
+               }
+               uid = (uid_t)strtonum(name, 0, UID_MAX, &errstr);
+               if (errstr != NULL) {
+                       if (errno == ERANGE)
+                               syslog(LOG_ERR, "UID %s is %s", name, errstr);
+                       else
+                               syslog(LOG_ERR, "unknown user: %s", name);
+                       return (1);
+               }
+               if (names == NULL) {
+                       errno = 0;
+                       pw = getpwuid(uid);
+                       if (pw == NULL) {
+                               if (errno != 0)
+                                       syslog(LOG_ERR, "getpwuid: %m");
+                               else
+                                       syslog(LOG_ERR, "unknown user: %s",
+                                           name);
+                               return (1);
+                       }
                }
-               cr->cr_uid = pw->pw_uid;
-               ngroups = XU_NGROUPS + 1;
-               if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
-                       syslog(LOG_ERR, "too many groups");
+       } else
+               uid = pw->pw_uid;
+       cr->cr_version = XUCRED_VERSION;
+       cr->cr_uid = uid;
+       if (names == NULL) {
                /*
-                * Compress out duplicate.
+                * Credentials specified as those of a user.
                 */
-               cr->cr_ngroups = ngroups - 1;
-               cr->cr_groups[0] = groups[0];
-               for (cnt = 2; cnt < ngroups; cnt++)
-                       cr->cr_groups[cnt - 1] = groups[cnt];
-               return;
-       }
-       /*
-        * Explicit credential specified as a colon separated list:
-        *      uid:gid:gid:...
-        */
-       if (pw != NULL)
-               cr->cr_uid = pw->pw_uid;
-       else if (isdigit(*name) || *name == '-')
-               cr->cr_uid = atoi(name);
-       else {
-               syslog(LOG_ERR, "unknown user: %s", name);
-               return;
-       }
-       cr->cr_ngroups = 0;
-       while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
-               name = strsep(&names, ":");
-               if (isdigit(*name) || *name == '-') {
-                       cr->cr_groups[cr->cr_ngroups++] = atoi(name);
-               } else {
-                       if ((gr = getgrnam(name)) == NULL) {
-                               syslog(LOG_ERR, "unknown group: %s", name);
-                               continue;
+               ngroups = XU_NGROUPS;
+               if (getgrouplist(pw->pw_name, pw->pw_gid, cr->cr_groups,
+                   &ngroups) < 0) {
+                       syslog(LOG_ERR, "user %s: too many groups",
+                           pw->pw_name);
+                       cr->cr_ngroups = XU_NGROUPS;
+               } else
+                       cr->cr_ngroups = ngroups;
+       } else {
+               /*
+                * Explicit credential specified as a colon separated list:
+                *      uid:gid:gid:...
+                */
+               cr->cr_ngroups = 0;
+               for (; names != NULL && *names != '\0'; cr->cr_ngroups++) {
+                       if (cr->cr_ngroups == XU_NGROUPS) {
+                               syslog(LOG_ERR, "user %s: too many groups",
+                                   username);
+                               break;
                        }
-                       cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
+                       name = strsep(&names, ":");
+                       errno = 0;
+                       gr = getgrnam(name);
+                       if (gr == NULL) {
+                               if (errno != 0) {
+                                       syslog(LOG_ERR, "getgrnam: %m");
+                                       return (1);
+                               }
+                               cr->cr_groups[cr->cr_ngroups] =
+                                   (gid_t)strtonum(name, 0, GID_MAX, &errstr);
+                               if (errstr != NULL) {
+                                       if (errno == ERANGE)
+                                               syslog(LOG_ERR, "GID %s "
+                                                   "is %s", name, errstr);
+                                       else
+                                               syslog(LOG_ERR, "unknown "
+                                                   "group: %s", name);
+                                       return (1);
+                               }
+                       } else
+                               cr->cr_groups[cr->cr_ngroups] = gr->gr_gid;
                }
        }
-       if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
-               syslog(LOG_ERR, "too many groups");
+       return (0);
 }
 
 #define        STRSIZ  (MNTNAMLEN+MNTPATHLEN+50)
>Release-Note:
>Audit-Trail:
>Unformatted:
_______________________________________________
freebsd-bugs@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"

Reply via email to