From 89621fe174cf95ae903df6ceab605bf24d696ac3 Mon Sep 17 00:00:00 2001 From: Marcel Telka Date: Fri, 21 Nov 2014 08:37:17 +0100 Subject: [PATCH] 5296 Support for more than 16 groups with AUTH_SYS Reviewed by: Gordon Ross Reviewed by: Josef 'Jeff' Sipek Approved by: Richard Lowe --- usr/src/cmd/fs.d/nfs/mountd/mountd.c | 157 +++++++++- usr/src/cmd/fs.d/nfs/mountd/mountd.h | 10 +- usr/src/cmd/fs.d/nfs/mountd/nfsauth.c | 65 +++-- usr/src/cmd/fs.d/nfs/mountd/nfsauth_xdr.c | 8 + usr/src/man/man5/nfssec.5 | 16 +- usr/src/uts/common/conf/param.c | 6 +- usr/src/uts/common/fs/nfs/nfs_auth.c | 456 +++++++++++++++++++----------- usr/src/uts/common/fs/nfs/nfs_auth_xdr.c | 8 + usr/src/uts/common/fs/nfs/nfs_server.c | 23 +- usr/src/uts/common/nfs/auth.h | 26 +- usr/src/uts/common/nfs/export.h | 13 +- usr/src/uts/common/nfs/nfs.h | 5 +- usr/src/uts/common/sys/param.h | 3 +- 13 files changed, 564 insertions(+), 232 deletions(-) diff --git a/usr/src/cmd/fs.d/nfs/mountd/mountd.c b/usr/src/cmd/fs.d/nfs/mountd/mountd.c index 852d6f484d..0d9ac76d30 100644 --- a/usr/src/cmd/fs.d/nfs/mountd/mountd.c +++ b/usr/src/cmd/fs.d/nfs/mountd/mountd.c @@ -17,9 +17,11 @@ * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END - * - * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +/* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ @@ -84,6 +86,7 @@ #include "smfcfg.h" #include #include +#include extern int daemonize_init(void); extern void daemonize_fini(int fd); @@ -106,9 +109,11 @@ static int getclientsflavors_old(share_t *, SVCXPRT *, struct netbuf **, static int getclientsflavors_new(share_t *, SVCXPRT *, struct netbuf **, struct nd_hostservlist **, int *); static int check_client_old(share_t *, SVCXPRT *, struct netbuf **, - struct nd_hostservlist **, int, uid_t, gid_t, uid_t *, gid_t *); + struct nd_hostservlist **, int, uid_t, gid_t, uint_t, gid_t *, uid_t *, + gid_t *, uint_t *, gid_t **); static int check_client_new(share_t *, SVCXPRT *, struct netbuf **, - struct nd_hostservlist **, int, uid_t, gid_t, uid_t *, gid_t *); + struct nd_hostservlist **, int, uid_t, gid_t, uint_t, gid_t *, uid_t *, + gid_t *i, uint_t *, gid_t **); static void mnt(struct svc_req *, SVCXPRT *); static void mnt_pathconf(struct svc_req *); static int mount(struct svc_req *r); @@ -147,6 +152,12 @@ typedef struct logging_data { static logging_data *logging_head = NULL; static logging_data *logging_tail = NULL; +/* + * Our copy of some system variables obtained using sysconf(3c) + */ +static long ngroups_max; /* _SC_NGROUPS_MAX */ +static long pw_size; /* _SC_GETPW_R_SIZE_MAX */ + /* ARGSUSED */ static void * nfsauth_svc(void *arg) @@ -542,6 +553,18 @@ main(int argc, char *argv[]) audit_mountd_setup(); /* BSM */ /* + * Get required system variables + */ + if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) { + syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX"); + exit(1); + } + if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) { + syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX"); + exit(1); + } + + /* * Set number of file descriptors to unlimited */ if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) { @@ -2161,14 +2184,58 @@ getclientsflavors_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb, int check_client(share_t *sh, struct netbuf *nb, struct nd_hostservlist *clnames, int flavor, uid_t clnt_uid, gid_t clnt_gid, - uid_t *srv_uid, gid_t *srv_gid) + uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid, gid_t *srv_gid, + uint_t *srv_ngids, gid_t **srv_gids) { if (newopts(sh->sh_opts)) return (check_client_new(sh, NULL, &nb, &clnames, flavor, - clnt_uid, clnt_gid, srv_uid, srv_gid)); + clnt_uid, clnt_gid, clnt_ngids, clnt_gids, srv_uid, srv_gid, + srv_ngids, srv_gids)); else return (check_client_old(sh, NULL, &nb, &clnames, flavor, - clnt_uid, clnt_gid, srv_uid, srv_gid)); + clnt_uid, clnt_gid, clnt_ngids, clnt_gids, srv_uid, srv_gid, + srv_ngids, srv_gids)); +} + +extern int _getgroupsbymember(const char *, gid_t[], int, int); + +/* + * Get supplemental groups for uid + */ +static int +getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps) +{ + struct passwd pwd; + char *pwbuf = alloca(pw_size); + gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t)); + int tmpngrps; + + if (getpwuid_r(uid, &pwd, pwbuf, pw_size) == NULL) + return (-1); + + tmpgrps[0] = pwd.pw_gid; + + tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1); + if (tmpngrps <= 0) { + syslog(LOG_WARNING, + "getusergroups(): Unable to get groups for user %s", + pwd.pw_name); + + return (-1); + } + + *grps = malloc(tmpngrps * sizeof (gid_t)); + if (*grps == NULL) { + syslog(LOG_ERR, + "getusergroups(): Memory allocation failed: %m"); + + return (-1); + } + + *ngrps = tmpngrps; + (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t)); + + return (0); } /* @@ -2252,7 +2319,8 @@ get_gid(char *value, gid_t *gid) static int check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb, struct nd_hostservlist **clnames, int flavor, uid_t clnt_uid, - gid_t clnt_gid, uid_t *srv_uid, gid_t *srv_gid) + gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid, + gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids) { char *opts, *p, *val; int match; /* Set when a flavor is matched */ @@ -2269,6 +2337,14 @@ check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb, return (0); } + /* + * If client provided 16 supplemental groups with AUTH_SYS, lookup + * locally for all of them + */ + if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS) + if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0) + perm |= NFSAUTH_GROUPS; + p = opts; match = AUTH_UNIX; @@ -2277,6 +2353,14 @@ check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb, case OPT_SECURE: match = AUTH_DES; + + if (perm & NFSAUTH_GROUPS) { + free(*srv_gids); + *srv_ngids = 0; + *srv_gids = NULL; + perm &= ~NFSAUTH_GROUPS; + } + break; case OPT_RO: @@ -2314,6 +2398,13 @@ check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb, perm |= NFSAUTH_ROOT; perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP; map_deny = B_FALSE; + + if (perm & NFSAUTH_GROUPS) { + free(*srv_gids); + *srv_ngids = 0; + *srv_gids = NULL; + perm &= ~NFSAUTH_GROUPS; + } } break; @@ -2385,6 +2476,14 @@ check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb, if (in_access_list(transp, nb, clnames, al)) { *srv_uid = srv; perm |= NFSAUTH_UIDMAP; + + if (perm & NFSAUTH_GROUPS) { + free(*srv_gids); + *srv_ngids = 0; + *srv_gids = NULL; + perm &= ~NFSAUTH_GROUPS; + } + break; } } @@ -2450,6 +2549,14 @@ check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb, if (in_access_list(transp, nb, clnames, al)) { *srv_gid = srv; perm |= NFSAUTH_GIDMAP; + + if (perm & NFSAUTH_GROUPS) { + free(*srv_gids); + *srv_ngids = 0; + *srv_gids = NULL; + perm &= ~NFSAUTH_GROUPS; + } + break; } } @@ -2572,7 +2679,8 @@ is_wrongsec(share_t *sh, SVCXPRT *transp, struct netbuf **nb, static int check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb, struct nd_hostservlist **clnames, int flavor, uid_t clnt_uid, - gid_t clnt_gid, uid_t *srv_uid, gid_t *srv_gid) + gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid, + gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids) { char *opts, *p, *val; char *lasts; @@ -2591,6 +2699,14 @@ check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb, return (0); } + /* + * If client provided 16 supplemental groups with AUTH_SYS, lookup + * locally for all of them + */ + if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS) + if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0) + perm |= NFSAUTH_GROUPS; + p = opts; while (*p) { @@ -2654,6 +2770,13 @@ check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb, perm |= NFSAUTH_ROOT; perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP; map_deny = B_FALSE; + + if (perm & NFSAUTH_GROUPS) { + free(*srv_gids); + *srv_gids = NULL; + *srv_ngids = 0; + perm &= ~NFSAUTH_GROUPS; + } } break; @@ -2725,6 +2848,14 @@ check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb, if (in_access_list(transp, nb, clnames, al)) { *srv_uid = srv; perm |= NFSAUTH_UIDMAP; + + if (perm & NFSAUTH_GROUPS) { + free(*srv_gids); + *srv_gids = NULL; + *srv_ngids = 0; + perm &= ~NFSAUTH_GROUPS; + } + break; } } @@ -2790,6 +2921,14 @@ check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb, if (in_access_list(transp, nb, clnames, al)) { *srv_gid = srv; perm |= NFSAUTH_GIDMAP; + + if (perm & NFSAUTH_GROUPS) { + free(*srv_gids); + *srv_gids = NULL; + *srv_ngids = 0; + perm &= ~NFSAUTH_GROUPS; + } + break; } } diff --git a/usr/src/cmd/fs.d/nfs/mountd/mountd.h b/usr/src/cmd/fs.d/nfs/mountd/mountd.h index 80b0f86bcc..54d352af79 100644 --- a/usr/src/cmd/fs.d/nfs/mountd/mountd.h +++ b/usr/src/cmd/fs.d/nfs/mountd/mountd.h @@ -20,11 +20,12 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ + /* - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. */ #ifndef _MOUNTD_H @@ -64,7 +65,8 @@ extern void *exmalloc(size_t); extern struct share *findentry(char *); extern int check_client(struct share *, struct netbuf *, - struct nd_hostservlist *, int, uid_t, gid_t, uid_t *, gid_t *); + struct nd_hostservlist *, int, uid_t, gid_t, uint_t, gid_t *, uid_t *, + gid_t *, uint_t *, gid_t **); extern struct nd_hostservlist *anon_client(char *host); /* diff --git a/usr/src/cmd/fs.d/nfs/mountd/nfsauth.c b/usr/src/cmd/fs.d/nfs/mountd/nfsauth.c index 0f681fa59e..34a971759b 100644 --- a/usr/src/cmd/fs.d/nfs/mountd/nfsauth.c +++ b/usr/src/cmd/fs.d/nfs/mountd/nfsauth.c @@ -20,11 +20,12 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ + /* - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. */ #include @@ -49,6 +50,7 @@ #include #include #include +#include #include "../lib/sharetab.h" #include "mountd.h" @@ -118,8 +120,10 @@ nfsauth_access(auth_req *argp, auth_res *result) } result->auth_perm = check_client(sh, &nbuf, clnames, argp->req_flavor, - argp->req_clnt_uid, argp->req_clnt_gid, &result->auth_srv_uid, - &result->auth_srv_gid); + argp->req_clnt_uid, argp->req_clnt_gid, argp->req_clnt_gids.len, + argp->req_clnt_gids.val, &result->auth_srv_uid, + &result->auth_srv_gid, &result->auth_srv_gids.len, + &result->auth_srv_gids.val); sharefree(sh); @@ -141,20 +145,16 @@ nfsauth_func(void *cookie, char *dataptr, size_t arg_size, { nfsauth_arg_t *ap; nfsauth_res_t res = {0}; - nfsauth_res_t *rp = &res; XDR xdrs_a; XDR xdrs_r; - caddr_t abuf = dataptr; - size_t absz = arg_size; - size_t rbsz = (size_t)(BYTES_PER_XDR_UNIT * 4); - char result[BYTES_PER_XDR_UNIT * 4]; - caddr_t rbuf = (caddr_t)&result; + size_t rbsz; + caddr_t rbuf; varg_t varg = {0}; /* * Decode the inbound door data, so we can look at the cmd. */ - xdrmem_create(&xdrs_a, abuf, absz, XDR_DECODE); + xdrmem_create(&xdrs_a, dataptr, arg_size, XDR_DECODE); if (!xdr_varg(&xdrs_a, &varg)) { /* * If the arguments can't be decoded, bail. @@ -173,10 +173,11 @@ nfsauth_func(void *cookie, char *dataptr, size_t arg_size, ap = &varg.arg_u.arg; break; - /* Additional arguments versions go here */ + /* Additional arguments versions go here */ default: syslog(LOG_ERR, gettext("Invalid args version")); + res.stat = NFSAUTH_DR_DECERR; goto encres; } @@ -184,40 +185,48 @@ nfsauth_func(void *cookie, char *dataptr, size_t arg_size, * Call the specified cmd */ switch (ap->cmd) { - case NFSAUTH_ACCESS: - nfsauth_access(&ap->areq, &rp->ares); - rp->stat = NFSAUTH_DR_OKAY; - break; - default: - rp->stat = NFSAUTH_DR_BADCMD; - break; + case NFSAUTH_ACCESS: + nfsauth_access(&ap->areq, &res.ares); + res.stat = NFSAUTH_DR_OKAY; + break; + default: + res.stat = NFSAUTH_DR_BADCMD; + break; } encres: /* * Free space used to decode the args */ - xdrs_a.x_op = XDR_FREE; - (void) xdr_varg(&xdrs_a, &varg); + xdr_free(xdr_varg, (char *)&varg); xdr_destroy(&xdrs_a); /* * Encode the results before passing thru door. - * - * The result (nfsauth_res_t) is always two int's, so we don't - * have to dynamically size (or allocate) the results buffer. */ + rbsz = xdr_sizeof(xdr_nfsauth_res, &res); + if (rbsz == 0) + goto failed; + rbuf = alloca(rbsz); + xdrmem_create(&xdrs_r, rbuf, rbsz, XDR_ENCODE); - if (!xdr_nfsauth_res(&xdrs_r, rp)) { + if (!xdr_nfsauth_res(&xdrs_r, &res)) { + xdr_destroy(&xdrs_r); +failed: + xdr_free(xdr_nfsauth_res, (char *)&res); /* * return only the status code */ - rp->stat = NFSAUTH_DR_EFAIL; + res.stat = NFSAUTH_DR_EFAIL; rbsz = sizeof (uint_t); - *rbuf = (uint_t)rp->stat; + rbuf = (caddr_t)&res.stat; + + goto out; } xdr_destroy(&xdrs_r); + xdr_free(xdr_nfsauth_res, (char *)&res); +out: (void) door_return((char *)rbuf, rbsz, NULL, 0); (void) door_return(NULL, 0, NULL, 0); /* NOTREACHED */ diff --git a/usr/src/cmd/fs.d/nfs/mountd/nfsauth_xdr.c b/usr/src/cmd/fs.d/nfs/mountd/nfsauth_xdr.c index f2a8426cc3..5a39231ab0 100644 --- a/usr/src/cmd/fs.d/nfs/mountd/nfsauth_xdr.c +++ b/usr/src/cmd/fs.d/nfs/mountd/nfsauth_xdr.c @@ -71,6 +71,10 @@ xdr_nfsauth_arg(XDR *xdrs, nfsauth_arg_t *argp) return (FALSE); if (!xdr_gid_t(xdrs, &argp->areq.req_clnt_gid)) return (FALSE); + if (!xdr_array(xdrs, (caddr_t *)&argp->areq.req_clnt_gids.val, + &argp->areq.req_clnt_gids.len, NGROUPS_UMAX, (uint_t)sizeof (gid_t), + xdr_gid_t)) + return (FALSE); return (TRUE); } @@ -85,5 +89,9 @@ xdr_nfsauth_res(XDR *xdrs, nfsauth_res_t *argp) return (FALSE); if (!xdr_gid_t(xdrs, &argp->ares.auth_srv_gid)) return (FALSE); + if (!xdr_array(xdrs, (caddr_t *)&argp->ares.auth_srv_gids.val, + &argp->ares.auth_srv_gids.len, NGROUPS_UMAX, (uint_t)sizeof (gid_t), + xdr_gid_t)) + return (FALSE); return (TRUE); } diff --git a/usr/src/man/man5/nfssec.5 b/usr/src/man/man5/nfssec.5 index 69ddf2b9b6..da696103ca 100644 --- a/usr/src/man/man5/nfssec.5 +++ b/usr/src/man/man5/nfssec.5 @@ -1,13 +1,13 @@ '\" te +.\" Copyright 2014 Nexenta Systems, Inc. All rights reserved. .\" Copyright (c) 2001, Sun Microsystems, Inc. All Rights Reserved .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. .\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the .\" fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner] -.TH NFSSEC 5 "Mar 16, 2009" +.TH NFSSEC 5 "Nov 20, 2014" .SH NAME nfssec \- overview of NFS security modes .SH DESCRIPTION -.sp .LP The \fBmount_nfs\fR(1M) and \fBshare_nfs\fR(1M) commands each provide a way to specify the security mode to be used on an \fBNFS\fR file system through the @@ -53,6 +53,13 @@ passed in the clear on the network, unauthenticated by the \fBNFS\fR server. This is the simplest security method and requires no additional administration. It is the default used by Solaris \fBNFS\fR Version 2 clients and Solaris \fBNFS\fR servers. +.sp +According to the ONC RPC specification (RFC 5531), \fBAUTH_SYS\fR +authentication supports up to 16 groups for a user only. To workaround this +limitation, in the case where the \fBNFS\fR client supplied 16 groups in +\fBAUTH_SYS\fR and \fBNGROUPS_MAX\fR is more than 16, the \fBNFS\fR server +will lookup the user's groups on the server instead of relying on the list of +groups provided by the \fBNFS\fR client via \fBAUTH_SYS\fR. .RE .sp @@ -109,13 +116,12 @@ Use null authentication (\fBAUTH_NONE\fR). \fBNFS\fR clients using \fBnobody\fR by \fBNFS\fR servers. A client using a security mode other than the one with which a Solaris \fBNFS\fR server shares the file system has its security mode mapped to \fBAUTH_NONE.\fR In this case, if the file system is -shared with \fBsec=\fR\fInone,\fR users from the client are mapped to the +shared with \fBsec=none,\fR users from the client are mapped to the anonymous user. The \fBNFS\fR security mode \fBnone\fR is supported by \fBshare_nfs\fR(1M), but not by \fBmount_nfs\fR(1M) or \fBautomount\fR(1M). .RE .SH FILES -.sp .ne 2 .na \fB\fB/etc/nfssec.conf\fR\fR @@ -125,13 +131,11 @@ anonymous user. The \fBNFS\fR security mode \fBnone\fR is supported by .RE .SH SEE ALSO -.sp .LP \fBautomount\fR(1M), \fBkclient\fR(1M), \fBmount_nfs\fR(1M), \fBshare_nfs\fR(1M), \fBrpc_clnt_auth\fR(3NSL), \fBsecure_rpc\fR(3NSL), \fBnfssec.conf\fR(4), \fBattributes\fR(5), \fBkerberos\fR(5) .SH NOTES -.sp .LP \fB/etc/nfssec.conf\fR lists the \fBNFS\fR security services. Do not edit this file. It is not intended to be user-configurable. See \fBkclient\fR(1M). diff --git a/usr/src/uts/common/conf/param.c b/usr/src/uts/common/conf/param.c index 505aceadac..6b3ba51f31 100644 --- a/usr/src/uts/common/conf/param.c +++ b/usr/src/uts/common/conf/param.c @@ -18,7 +18,9 @@ * * CDDL HEADER END */ + /* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. */ @@ -592,10 +594,6 @@ param_calc(int platform_max_nprocs) cmn_err(CE_NOTE, "maxusers limited to %d", MAX_MAXUSERS); } - if (ngroups_max > NGROUPS_MAX_DEFAULT) - cmn_err(CE_WARN, "ngroups_max of %d > %d, NFS AUTH_SYS will" - " not work properly", ngroups_max, NGROUPS_MAX_DEFAULT); - #ifdef DEBUG /* * The purpose of maxusers is to prevent memory overcommit. diff --git a/usr/src/uts/common/fs/nfs/nfs_auth.c b/usr/src/uts/common/fs/nfs/nfs_auth.c index d20780ebba..18d358795b 100644 --- a/usr/src/uts/common/fs/nfs/nfs_auth.c +++ b/usr/src/uts/common/fs/nfs/nfs_auth.c @@ -18,9 +18,10 @@ * * CDDL HEADER END */ + /* - * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. */ #include @@ -177,7 +178,7 @@ void mountd_args(uint_t did) { mutex_enter(&mountd_lock); - if (mountd_dh) + if (mountd_dh != NULL) door_ki_rele(mountd_dh); mountd_dh = door_ki_lookup(did); mutex_exit(&mountd_lock); @@ -317,11 +318,11 @@ addrmask(struct netbuf *addr, struct netbuf *mask) */ int nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req, - cred_t *cr, uid_t *uid, gid_t *gid) + cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids) { int access; - access = nfsauth_access(exi, req, cr, uid, gid); + access = nfsauth_access(exi, req, cr, uid, gid, ngids, gids); /* * There are cases that the server needs to allow the client @@ -370,17 +371,14 @@ sys_log(const char *msg) static bool_t nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor, struct netbuf *addr, int *access, uid_t clnt_uid, gid_t clnt_gid, - uid_t *srv_uid, gid_t *srv_gid) + uint_t clnt_gids_cnt, const gid_t *clnt_gids, uid_t *srv_uid, + gid_t *srv_gid, uint_t *srv_gids_cnt, gid_t **srv_gids) { varg_t varg = {0}; nfsauth_res_t res = {0}; - XDR xdrs_a; - XDR xdrs_r; + XDR xdrs; size_t absz; caddr_t abuf; - size_t rbsz = (size_t)(BYTES_PER_XDR_UNIT * 4); - char result[BYTES_PER_XDR_UNIT * 4] = {0}; - caddr_t rbuf = (caddr_t)&result; int last = 0; door_arg_t da; door_info_t di; @@ -392,35 +390,7 @@ nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor, * so we need to call the nfsauth service in the * mount daemon. */ -retry: - mutex_enter(&mountd_lock); - dh = mountd_dh; - if (dh) - door_ki_hold(dh); - mutex_exit(&mountd_lock); - - if (dh == NULL) { - /* - * The rendezvous point has not been established yet ! - * This could mean that either mountd(1m) has not yet - * been started or that _this_ routine nuked the door - * handle after receiving an EINTR for a REVOKED door. - * - * Returning NFSAUTH_DROP will cause the NFS client - * to retransmit the request, so let's try to be more - * rescillient and attempt for ntries before we bail. - */ - if (++ntries % NFSAUTH_DR_TRYCNT) { - delay(hz); - goto retry; - } - - sys_log("nfsauth: mountd has not established door"); - *access = NFSAUTH_DROP; - return (FALSE); - } - ntries = 0; varg.vers = V_PROTO; varg.arg_u.arg.cmd = NFSAUTH_ACCESS; varg.arg_u.arg.areq.req_client.n_len = addr->len; @@ -430,6 +400,10 @@ retry: varg.arg_u.arg.areq.req_flavor = flavor; varg.arg_u.arg.areq.req_clnt_uid = clnt_uid; varg.arg_u.arg.areq.req_clnt_gid = clnt_gid; + varg.arg_u.arg.areq.req_clnt_gids.len = clnt_gids_cnt; + varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)clnt_gids; + + DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg); /* * Setup the XDR stream for encoding the arguments. Notice that @@ -440,153 +414,175 @@ retry: * info _or_ properly encode the arguments, there's really no * point in continuting, so we fail the request. */ - DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg); - if ((absz = xdr_sizeof(xdr_varg, (void *)&varg)) == 0) { - door_ki_rele(dh); + if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) { *access = NFSAUTH_DENIED; return (FALSE); } abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP); - xdrmem_create(&xdrs_a, abuf, absz, XDR_ENCODE); - if (!xdr_varg(&xdrs_a, &varg)) { - door_ki_rele(dh); + xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE); + if (!xdr_varg(&xdrs, &varg)) { + XDR_DESTROY(&xdrs); goto fail; } - XDR_DESTROY(&xdrs_a); + XDR_DESTROY(&xdrs); /* - * The result (nfsauth_res_t) is always four int's, so we don't - * have to dynamically size (or allocate) the results buffer. - * Now that we've got what we need, we prep the door arguments - * and place the call. + * Prepare the door arguments + * + * We don't know the size of the message the daemon + * will pass back to us. By setting rbuf to NULL, + * we force the door code to allocate a buf of the + * appropriate size. We must set rsize > 0, however, + * else the door code acts as if no response was + * expected and doesn't pass the data to us. */ da.data_ptr = (char *)abuf; da.data_size = absz; da.desc_ptr = NULL; da.desc_num = 0; - da.rbuf = (char *)rbuf; - da.rsize = rbsz; + da.rbuf = NULL; + da.rsize = 1; - switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) { - case 0: /* Success */ - if (da.data_ptr != da.rbuf && da.data_size == 0) { - /* - * The door_return that contained the data - * failed ! We're here because of the 2nd - * door_return (w/o data) such that we can - * get control of the thread (and exit - * gracefully). - */ - DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil, - door_arg_t *, &da); - door_ki_rele(dh); - goto fail; +retry: + mutex_enter(&mountd_lock); + dh = mountd_dh; + if (dh != NULL) + door_ki_hold(dh); + mutex_exit(&mountd_lock); - } else if (rbuf != da.rbuf) { - /* - * The only time this should be true - * is iff userland wanted to hand us - * a bigger response than what we - * expect; that should not happen - * (nfsauth_res_t is only 4 int's), - * but we check nevertheless. - */ - rbuf = da.rbuf; - rbsz = da.rsize; + if (dh == NULL) { + /* + * The rendezvous point has not been established yet! + * This could mean that either mountd(1m) has not yet + * been started or that _this_ routine nuked the door + * handle after receiving an EINTR for a REVOKED door. + * + * Returning NFSAUTH_DROP will cause the NFS client + * to retransmit the request, so let's try to be more + * rescillient and attempt for ntries before we bail. + */ + if (++ntries % NFSAUTH_DR_TRYCNT) { + delay(hz); + goto retry; + } - } else if (rbsz > da.data_size) { - /* - * We were expecting four int's; but if - * userland fails in encoding the XDR - * stream, we detect that here, since - * the mountd forces down only one byte - * in such scenario. - */ - door_ki_rele(dh); - goto fail; - } - door_ki_rele(dh); - break; + kmem_free(abuf, absz); + + sys_log("nfsauth: mountd has not established door"); + *access = NFSAUTH_DROP; + return (FALSE); + } + + ntries = 0; + + /* + * Now that we've got what we need, place the call. + */ + switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) { + case 0: /* Success */ + door_ki_rele(dh); - case EAGAIN: + if (da.data_ptr == NULL && da.data_size == 0) { /* - * Server out of resources; back off for a bit + * The door_return that contained the data + * failed! We're here because of the 2nd + * door_return (w/o data) such that we can + * get control of the thread (and exit + * gracefully). */ + DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil, + door_arg_t *, &da); + goto fail; + } + + break; + + case EAGAIN: + /* + * Server out of resources; back off for a bit + */ + door_ki_rele(dh); + delay(hz); + goto retry; + /* NOTREACHED */ + + case EINTR: + if (!door_ki_info(dh, &di)) { door_ki_rele(dh); - kmem_free(abuf, absz); - delay(hz); - goto retry; - /* NOTREACHED */ - case EINTR: - if (!door_ki_info(dh, &di)) { - if (di.di_attributes & DOOR_REVOKED) { - /* - * The server barfed and revoked - * the (existing) door on us; we - * want to wait to give smf(5) a - * chance to restart mountd(1m) - * and establish a new door handle. - */ - mutex_enter(&mountd_lock); - if (dh == mountd_dh) - mountd_dh = NULL; - mutex_exit(&mountd_lock); - door_ki_rele(dh); - kmem_free(abuf, absz); - delay(hz); - goto retry; - } + if (di.di_attributes & DOOR_REVOKED) { /* - * If the door was _not_ revoked on us, - * then more than likely we took an INTR, - * so we need to fail the operation. + * The server barfed and revoked + * the (existing) door on us; we + * want to wait to give smf(5) a + * chance to restart mountd(1m) + * and establish a new door handle. */ - door_ki_rele(dh); - goto fail; - } - /* - * The only failure that can occur from getting - * the door info is EINVAL, so we let the code - * below handle it. - */ - /* FALLTHROUGH */ - - case EBADF: - case EINVAL: - default: - /* - * If we have a stale door handle, give smf a last - * chance to start it by sleeping for a little bit. - * If we're still hosed, we'll fail the call. - * - * Since we're going to reacquire the door handle - * upon the retry, we opt to sleep for a bit and - * _not_ to clear mountd_dh. If mountd restarted - * and was able to set mountd_dh, we should see - * the new instance; if not, we won't get caught - * up in the retry/DELAY loop. - */ - door_ki_rele(dh); - if (!last) { + mutex_enter(&mountd_lock); + if (dh == mountd_dh) { + door_ki_rele(mountd_dh); + mountd_dh = NULL; + } + mutex_exit(&mountd_lock); delay(hz); - last++; goto retry; } - sys_log("nfsauth: stale mountd door handle"); + /* + * If the door was _not_ revoked on us, + * then more than likely we took an INTR, + * so we need to fail the operation. + */ goto fail; + } + /* + * The only failure that can occur from getting + * the door info is EINVAL, so we let the code + * below handle it. + */ + /* FALLTHROUGH */ + + case EBADF: + case EINVAL: + default: + /* + * If we have a stale door handle, give smf a last + * chance to start it by sleeping for a little bit. + * If we're still hosed, we'll fail the call. + * + * Since we're going to reacquire the door handle + * upon the retry, we opt to sleep for a bit and + * _not_ to clear mountd_dh. If mountd restarted + * and was able to set mountd_dh, we should see + * the new instance; if not, we won't get caught + * up in the retry/DELAY loop. + */ + door_ki_rele(dh); + if (!last) { + delay(hz); + last++; + goto retry; + } + sys_log("nfsauth: stale mountd door handle"); + goto fail; } + ASSERT(da.rbuf != NULL); + /* * No door errors encountered; setup the XDR stream for decoding * the results. If we fail to decode the results, we've got no * other recourse than to fail the request. */ - xdrmem_create(&xdrs_r, rbuf, rbsz, XDR_DECODE); - if (!xdr_nfsauth_res(&xdrs_r, &res)) + xdrmem_create(&xdrs, da.rbuf, da.rsize, XDR_DECODE); + if (!xdr_nfsauth_res(&xdrs, &res)) { + xdr_free(xdr_nfsauth_res, (char *)&res); + XDR_DESTROY(&xdrs); + kmem_free(da.rbuf, da.rsize); goto fail; - XDR_DESTROY(&xdrs_r); + } + XDR_DESTROY(&xdrs); + kmem_free(da.rbuf, da.rsize); DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res); switch (res.stat) { @@ -594,13 +590,18 @@ retry: *access = res.ares.auth_perm; *srv_uid = res.ares.auth_srv_uid; *srv_gid = res.ares.auth_srv_gid; - kmem_free(abuf, absz); + *srv_gids_cnt = res.ares.auth_srv_gids.len; + *srv_gids = kmem_alloc(*srv_gids_cnt * sizeof (gid_t), + KM_SLEEP); + bcopy(res.ares.auth_srv_gids.val, *srv_gids, + *srv_gids_cnt * sizeof (gid_t)); break; case NFSAUTH_DR_EFAIL: case NFSAUTH_DR_DECERR: case NFSAUTH_DR_BADCMD: default: + xdr_free(xdr_nfsauth_res, (char *)&res); fail: *access = NFSAUTH_DENIED; kmem_free(abuf, absz); @@ -608,6 +609,9 @@ fail: /* NOTREACHED */ } + xdr_free(xdr_nfsauth_res, (char *)&res); + kmem_free(abuf, absz); + return (TRUE); } @@ -655,6 +659,8 @@ nfsauth_refresh_thread(void) */ while ((ran = list_remove_head(&ren->ren_authlist))) { + uint_t ngids; + gid_t *gids; struct auth_cache *p = ran->ran_auth; ASSERT(p != NULL); @@ -715,7 +721,8 @@ nfsauth_refresh_thread(void) retrieval = nfsauth_retrieve(exi, p->auth_netid, p->auth_flavor, &p->auth_addr, &access, p->auth_clnt_uid, p->auth_clnt_gid, - &p->auth_srv_uid, &p->auth_srv_gid); + p->auth_clnt_ngids, p->auth_clnt_gids, + &p->auth_srv_uid, &p->auth_srv_gid, &ngids, &gids); /* * This can only be set in one other place @@ -728,6 +735,8 @@ nfsauth_refresh_thread(void) if (p->auth_state == NFS_AUTH_INVALID) { mutex_exit(&p->auth_lock); nfsauth_remove_dead_entry(p); + if (retrieval == TRUE) + kmem_free(gids, ngids * sizeof (gid_t)); } else { /* * If we got an error, do not reset the @@ -737,6 +746,12 @@ nfsauth_refresh_thread(void) */ if (retrieval == TRUE) { p->auth_access = access; + + kmem_free(p->auth_srv_gids, + p->auth_srv_ngids * sizeof (gid_t)); + p->auth_srv_ngids = ngids; + p->auth_srv_gids = gids; + p->auth_freshness = gethrestime_sec(); } p->auth_state = NFS_AUTH_FRESH; @@ -761,13 +776,14 @@ nfsauth_refresh_thread(void) */ static int nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor, - cred_t *cr, uid_t *uid, gid_t *gid) + cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids) { struct netbuf *taddrmask; struct netbuf addr; struct netbuf *claddr; struct auth_cache **head; struct auth_cache *p; + struct auth_cache *prev = NULL; int access; time_t refresh; @@ -776,6 +792,8 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor, uid_t tmpuid; gid_t tmpgid; + uint_t tmpngids; + gid_t *tmpgids; ASSERT(cr != NULL); @@ -805,9 +823,53 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor, crgetuid(cr) == p->auth_clnt_uid && crgetgid(cr) == p->auth_clnt_gid) break; + prev = p; } if (p != NULL) { + /* + * In a case the client's supplemental groups changed we need + * to discard the auth_cache entry and re-retrieve it. + */ + mutex_enter(&p->auth_lock); + if (p->auth_clnt_ngids != crgetngroups(cr) || + bcmp(p->auth_clnt_gids, crgetgroups(cr), + p->auth_clnt_ngids * sizeof (gid_t))) { + auth_state_t prev_state = p->auth_state; + + p->auth_state = NFS_AUTH_INVALID; + mutex_exit(&p->auth_lock); + + if (prev_state == NFS_AUTH_FRESH) { + if (rw_tryupgrade(&exi->exi_cache_lock) == 0) { + struct auth_cache *tmp; + + rw_exit(&exi->exi_cache_lock); + rw_enter(&exi->exi_cache_lock, + RW_WRITER); + + prev = NULL; + for (tmp = *head; tmp != NULL; + tmp = tmp->auth_next) { + if (p == tmp) + break; + prev = p; + } + } + + if (prev == NULL) + exi->exi_cache[hash(&addr)] = + p->auth_next; + else + prev->auth_next = p->auth_next; + + nfsauth_free_node(p); + } + + goto retrieve; + } + mutex_exit(&p->auth_lock); + nfsauth_cache_hit++; refresh = gethrestime_sec() - p->auth_freshness; @@ -889,6 +951,11 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor, *uid = p->auth_srv_uid; if (gid != NULL) *gid = p->auth_srv_gid; + if (ngids != NULL && gids != NULL) { + *ngids = p->auth_srv_ngids; + *gids = kmem_alloc(*ngids * sizeof (gid_t), KM_SLEEP); + bcopy(p->auth_srv_gids, *gids, *ngids * sizeof (gid_t)); + } p->auth_time = gethrestime_sec(); @@ -898,13 +965,19 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor, return (access); } +retrieve: rw_exit(&exi->exi_cache_lock); nfsauth_cache_miss++; if (!nfsauth_retrieve(exi, svc_getnetid(req->rq_xprt), flavor, - &addr, &access, crgetuid(cr), crgetgid(cr), &tmpuid, &tmpgid)) { + &addr, &access, crgetuid(cr), crgetgid(cr), crgetngroups(cr), + crgetgroups(cr), &tmpuid, &tmpgid, &tmpngids, &tmpgids)) { kmem_free(addr.buf, addr.len); + if (ngids != NULL && gids != NULL) { + *ngids = 0; + *gids = NULL; + } return (access); } @@ -912,19 +985,39 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor, *uid = tmpuid; if (gid != NULL) *gid = tmpgid; + if (ngids != NULL && gids != NULL) { + *ngids = tmpngids; + *gids = tmpgids; + + /* + * We need a copy of gids for the auth_cache entry + */ + tmpgids = kmem_alloc(tmpngids * sizeof (gid_t), KM_NOSLEEP); + if (tmpgids != NULL) + bcopy(*gids, tmpgids, tmpngids * sizeof (gid_t)); + } /* * Now cache the result on the cache chain * for this export (if there's enough memory) */ p = kmem_cache_alloc(exi_cache_handle, KM_NOSLEEP); - if (p != NULL) { + if (p != NULL) + p->auth_clnt_gids = kmem_alloc( + crgetngroups(cr) * sizeof (gid_t), KM_NOSLEEP); + if (p != NULL && (tmpngids == 0 || tmpgids != NULL) && + (crgetngroups(cr) == 0 || p->auth_clnt_gids != NULL)) { p->auth_addr = addr; p->auth_flavor = flavor; p->auth_clnt_uid = crgetuid(cr); p->auth_clnt_gid = crgetgid(cr); + p->auth_clnt_ngids = crgetngroups(cr); + bcopy(crgetgroups(cr), p->auth_clnt_gids, + p->auth_clnt_ngids * sizeof (gid_t)); p->auth_srv_uid = tmpuid; p->auth_srv_gid = tmpgid; + p->auth_srv_ngids = tmpngids; + p->auth_srv_gids = tmpgids; p->auth_access = access; p->auth_time = p->auth_freshness = gethrestime_sec(); p->auth_state = NFS_AUTH_FRESH; @@ -937,6 +1030,14 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor, rw_exit(&exi->exi_cache_lock); } else { kmem_free(addr.buf, addr.len); + if (tmpgids != NULL) + kmem_free(tmpgids, tmpngids * sizeof (gid_t)); + if (p != NULL) { + if (p->auth_clnt_gids != NULL) + kmem_free(p->auth_clnt_gids, + crgetngroups(cr) * sizeof (gid_t)); + kmem_cache_free(exi_cache_handle, p); + } } return (access); @@ -967,14 +1068,15 @@ nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req, return (NFSAUTH_RW); } - access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL); + access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL, NULL, + NULL); return (access); } int nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr, - uid_t *uid, gid_t *gid) + uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids) { int access, mapaccess; struct secinfo *sp; @@ -1021,17 +1123,22 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr, * This might get overriden later in nfsauth_cache_get(). */ if (crgetuid(cr) == 0) { - if (uid) + if (uid != NULL) *uid = exi->exi_export.ex_anon; - if (gid) + if (gid != NULL) *gid = exi->exi_export.ex_anon; } else { - if (uid) + if (uid != NULL) *uid = crgetuid(cr); - if (gid) + if (gid != NULL) *gid = crgetgid(cr); } + if (ngids != NULL) + *ngids = 0; + if (gids != NULL) + *gids = NULL; + /* * If the flavor is in the ex_secinfo list, but not an explicitly * shared flavor by the user, it is a result of the nfsv4 server @@ -1054,10 +1161,12 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr, return (mapaccess | NFSAUTH_RO); /* - * Optimize if there are no lists + * Optimize if there are no lists. + * We cannot optimize for AUTH_SYS with NGRPS (16) supplemental groups. */ perm = sp[i].s_flags; - if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0) { + if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0 && (ngroups_max <= NGRPS || + flavor != AUTH_SYS || crgetngroups(cr) < NGRPS)) { perm &= ~M_4SEC_EXPORTED; if (perm == M_RO) return (mapaccess | NFSAUTH_RO); @@ -1065,7 +1174,19 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr, return (mapaccess | NFSAUTH_RW); } - access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid); + access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid, ngids, gids); + + /* + * For both NFSAUTH_DENIED and NFSAUTH_WRONGSEC we do not care about + * the supplemental groups. + */ + if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) { + if (ngids != NULL && gids != NULL) { + kmem_free(*gids, *ngids * sizeof (gid_t)); + *ngids = 0; + *gids = NULL; + } + } /* * Client's security flavor doesn't match with "ro" or @@ -1078,7 +1199,7 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr, if (authnone_entry != -1) { mapaccess = NFSAUTH_MAPNONE; access = nfsauth_cache_get(exi, req, AUTH_NONE, cr, - NULL, NULL); + NULL, NULL, NULL, NULL); } else { /* * Check for AUTH_NONE presence. @@ -1087,7 +1208,8 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr, if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) { mapaccess = NFSAUTH_MAPNONE; access = nfsauth_cache_get(exi, req, - AUTH_NONE, cr, NULL, NULL); + AUTH_NONE, cr, NULL, NULL, NULL, + NULL); break; } } @@ -1106,8 +1228,10 @@ nfsauth_free_node(struct auth_cache *p) if (p->auth_netid != NULL) kmem_free(p->auth_netid, strlen(p->auth_netid) + 1); kmem_free(p->auth_addr.buf, p->auth_addr.len); + kmem_free(p->auth_clnt_gids, p->auth_clnt_ngids * sizeof (gid_t)); + kmem_free(p->auth_srv_gids, p->auth_srv_ngids * sizeof (gid_t)); mutex_destroy(&p->auth_lock); - kmem_cache_free(exi_cache_handle, (void *)p); + kmem_cache_free(exi_cache_handle, p); } /* diff --git a/usr/src/uts/common/fs/nfs/nfs_auth_xdr.c b/usr/src/uts/common/fs/nfs/nfs_auth_xdr.c index 8fcec0e7b7..1ccba37a21 100644 --- a/usr/src/uts/common/fs/nfs/nfs_auth_xdr.c +++ b/usr/src/uts/common/fs/nfs/nfs_auth_xdr.c @@ -70,6 +70,10 @@ xdr_nfsauth_arg(XDR *xdrs, nfsauth_arg_t *argp) return (FALSE); if (!xdr_gid_t(xdrs, &argp->areq.req_clnt_gid)) return (FALSE); + if (!xdr_array(xdrs, (caddr_t *)&argp->areq.req_clnt_gids.val, + &argp->areq.req_clnt_gids.len, NGROUPS_UMAX, (uint_t)sizeof (gid_t), + xdr_gid_t)) + return (FALSE); return (TRUE); } @@ -84,5 +88,9 @@ xdr_nfsauth_res(XDR *xdrs, nfsauth_res_t *argp) return (FALSE); if (!xdr_gid_t(xdrs, &argp->ares.auth_srv_gid)) return (FALSE); + if (!xdr_array(xdrs, (caddr_t *)&argp->ares.auth_srv_gids.val, + &argp->ares.auth_srv_gids.len, NGROUPS_UMAX, (uint_t)sizeof (gid_t), + xdr_gid_t)) + return (FALSE); return (TRUE); } diff --git a/usr/src/uts/common/fs/nfs/nfs_server.c b/usr/src/uts/common/fs/nfs/nfs_server.c index ca3d7d8bb9..7e94c62734 100644 --- a/usr/src/uts/common/fs/nfs/nfs_server.c +++ b/usr/src/uts/common/fs/nfs/nfs_server.c @@ -2019,6 +2019,8 @@ checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok, uid_t uid; gid_t gid; + uint_t ngids; + gid_t *gids; /* * Check for privileged port number @@ -2077,7 +2079,7 @@ checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok, /* * Check if the auth flavor is valid for this export */ - access = nfsauth_access(exi, req, cr, &uid, &gid); + access = nfsauth_access(exi, req, cr, &uid, &gid, &ngids, &gids); if (access & NFSAUTH_DROP) return (-1); /* drop the request */ @@ -2113,6 +2115,9 @@ checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok, return (0); } + if (rpcflavor != AUTH_SYS) + kmem_free(gids, ngids * sizeof (gid_t)); + switch (rpcflavor) { case AUTH_NONE: anon_res = crsetugid(cr, exi->exi_export.ex_anon, @@ -2152,8 +2157,12 @@ checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok, exi->exi_export.ex_anon, exi->exi_export.ex_anon); (void) crsetgroups(cr, 0, NULL); + } else if (access & NFSAUTH_GROUPS) { + (void) crsetgroups(cr, ngids, gids); } + kmem_free(gids, ngids * sizeof (gid_t)); + break; case AUTH_DES: @@ -2271,6 +2280,8 @@ checkauth4(struct compound_state *cs, struct svc_req *req) uid_t uid; gid_t gid; + uint_t ngids; + gid_t *gids; exi = cs->exi; cr = cs->cr; @@ -2311,7 +2322,8 @@ checkauth4(struct compound_state *cs, struct svc_req *req) * Check the access right per auth flavor on the vnode of * this export for the given request. */ - access = nfsauth4_access(cs->exi, cs->vp, req, cr, &uid, &gid); + access = nfsauth4_access(cs->exi, cs->vp, req, cr, &uid, &gid, &ngids, + &gids); if (access & NFSAUTH_WRONGSEC) return (-2); /* no access for this security flavor */ @@ -2341,6 +2353,9 @@ checkauth4(struct compound_state *cs, struct svc_req *req) * return 1 on success or 0 on failure */ + if (rpcflavor != AUTH_SYS) + kmem_free(gids, ngids * sizeof (gid_t)); + switch (rpcflavor) { case AUTH_NONE: anon_res = crsetugid(cr, exi->exi_export.ex_anon, @@ -2380,8 +2395,12 @@ checkauth4(struct compound_state *cs, struct svc_req *req) exi->exi_export.ex_anon, exi->exi_export.ex_anon); (void) crsetgroups(cr, 0, NULL); + } if (access & NFSAUTH_GROUPS) { + (void) crsetgroups(cr, ngids, gids); } + kmem_free(gids, ngids * sizeof (gid_t)); + break; default: diff --git a/usr/src/uts/common/nfs/auth.h b/usr/src/uts/common/nfs/auth.h index 365b1ff6d7..5293e3fdd1 100644 --- a/usr/src/uts/common/nfs/auth.h +++ b/usr/src/uts/common/nfs/auth.h @@ -18,12 +18,14 @@ * * CDDL HEADER END */ + /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ + /* - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. */ #ifndef _AUTH_H @@ -50,6 +52,7 @@ * int req_flavor; # auth flavor * uid_t req_clnt_uid; # client's uid * gid_t req_clnt_gid; # client's gid + * gid_t req_clnt_gids<>; # client's supplemental groups * }; * * const NFSAUTH_DENIED = 0x01; # Access denied @@ -60,6 +63,7 @@ * # try a different flavor * const NFSAUTH_UIDMAP = 0x100; # uid mapped * const NFSAUTH_GIDMAP = 0x200; # gid mapped + * const NFSAUTH_GROUPS = 0x400; # translated supplemental groups * # * # The following are not part of the protocol. * # @@ -68,9 +72,10 @@ * const NFSAUTH_LIMITED = 0x80; # Access limited to visible nodes * * struct auth_res { - * int auth_perm; - * uid_t auth_srv_uid; - * gid_t auth_srv_gid; + * int auth_perm; + * uid_t auth_srv_uid; # translated uid + * gid_t auth_srv_gid; # translated gid + * gid_t auth_srv_gids<>; # translated supplemental groups * }; * * program NFSAUTH_PROG { @@ -113,6 +118,7 @@ extern "C" { #define NFSAUTH_LIMITED 0x80 #define NFSAUTH_UIDMAP 0x100 #define NFSAUTH_GIDMAP 0x200 +#define NFSAUTH_GROUPS 0x400 struct auth_req { netobj req_client; @@ -121,6 +127,10 @@ struct auth_req { int req_flavor; uid_t req_clnt_uid; gid_t req_clnt_gid; + struct { + uint_t len; + gid_t *val; + } req_clnt_gids; }; typedef struct auth_req auth_req; @@ -128,6 +138,10 @@ struct auth_res { int auth_perm; uid_t auth_srv_uid; gid_t auth_srv_gid; + struct { + uint_t len; + gid_t *val; + } auth_srv_gids; }; typedef struct auth_res auth_res; diff --git a/usr/src/uts/common/nfs/export.h b/usr/src/uts/common/nfs/export.h index 03851c1fb8..143b98fd8e 100644 --- a/usr/src/uts/common/nfs/export.h +++ b/usr/src/uts/common/nfs/export.h @@ -18,9 +18,10 @@ * * CDDL HEADER END */ + /* - * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -256,8 +257,12 @@ struct auth_cache { int auth_flavor; uid_t auth_clnt_uid; gid_t auth_clnt_gid; + uint_t auth_clnt_ngids; + gid_t *auth_clnt_gids; uid_t auth_srv_uid; gid_t auth_srv_gid; + uint_t auth_srv_ngids; + gid_t *auth_srv_gids; int auth_access; time_t auth_time; time_t auth_freshness; @@ -542,12 +547,12 @@ typedef struct exp_visible exp_visible_t; #define rdonly4(req, cs) \ (vn_is_readonly((cs)->vp) || \ (nfsauth4_access((cs)->exi, (cs)->vp, (req), (cs)->basecr, NULL, \ - NULL) & (NFSAUTH_RO | NFSAUTH_LIMITED))) + NULL, NULL, NULL) & (NFSAUTH_RO | NFSAUTH_LIMITED))) extern int nfsauth4_access(struct exportinfo *, vnode_t *, - struct svc_req *, cred_t *, uid_t *, gid_t *); + struct svc_req *, cred_t *, uid_t *, gid_t *, uint_t *, gid_t **); extern int nfsauth4_secinfo_access(struct exportinfo *, - struct svc_req *, int, int, cred_t *); + struct svc_req *, int, int, cred_t *); extern int nfs_fhbcmp(char *, char *, int); extern int nfs_exportinit(void); extern void nfs_exportfini(void); diff --git a/usr/src/uts/common/nfs/nfs.h b/usr/src/uts/common/nfs/nfs.h index b85fe98b11..681057e708 100644 --- a/usr/src/uts/common/nfs/nfs.h +++ b/usr/src/uts/common/nfs/nfs.h @@ -18,10 +18,11 @@ * * CDDL HEADER END */ + /* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ @@ -938,7 +939,7 @@ extern int nfslookup(vnode_t *, char *, vnode_t **, struct pathname *, int, vnode_t *, cred_t *, int); extern void sv_free(struct servinfo *); extern int nfsauth_access(struct exportinfo *, struct svc_req *, cred_t *, - uid_t *, gid_t *); + uid_t *, gid_t *, uint_t *, gid_t **); extern void nfsauth_init(void); extern void nfsauth_fini(void); extern int nfs_setopts(vnode_t *, model_t, struct nfs_args *); diff --git a/usr/src/uts/common/sys/param.h b/usr/src/uts/common/sys/param.h index 50f6084cfb..01c25610dd 100644 --- a/usr/src/uts/common/sys/param.h +++ b/usr/src/uts/common/sys/param.h @@ -18,7 +18,9 @@ * * CDDL HEADER END */ + /* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. */ @@ -131,7 +133,6 @@ extern "C" { /* * NGROUPS_MAX_DEFAULT: *MUST* match NGROUPS_MAX value in limits.h. - * Remember that the NFS protocol must rev. before this can be increased */ #define NGROUPS_MAX_DEFAULT 16 -- 2.11.4.GIT