/** * @brief Validate export permissions * * @param[in] req Incoming request. * * @return NFS4_OK if successful, NFS4ERR_ACCESS or NFS4ERR_WRONGSEC otherwise. * */ nfsstat4 nfs4_export_check_access(struct svc_req *req) { xprt_type_t xprt_type = svc_get_xprt_type(req->rq_xprt); int port = get_port(op_ctx->caller_addr); LogMidDebugAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "nfs4_export_check_access about to call export_check_access"); export_check_access(); /* Check if any access at all */ if ((op_ctx->export_perms->options & EXPORT_OPTION_ACCESS_TYPE) == 0) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "Access not allowed on Export_Id %d %s for client %s", op_ctx->export->export_id, op_ctx->export->fullpath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; }
/** * @brief Validate export permissions * * @param[in] req Incoming request. * * @return NFS4_OK if successful, NFS4ERR_ACCESS or NFS4ERR_WRONGSEC otherwise. * */ nfsstat4 nfs4_export_check_access(struct svc_req *req) { xprt_type_t xprt_type = svc_get_xprt_type(req->rq_xprt); int port = get_port(op_ctx->caller_addr); LogMidDebugAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "nfs4_export_check_access about to call export_check_access"); export_check_access(); /* Check if any access at all */ if ((op_ctx->export_perms->options & EXPORT_OPTION_ACCESS_MASK) == 0) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "Access not allowed on Export_Id %d %s for client %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->fullpath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; } /* Check protocol version */ if ((op_ctx->export_perms->options & EXPORT_OPTION_NFSV4) == 0) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "NFS4 not allowed on Export_Id %d %s for client %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->fullpath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; } /* Check transport type */ if (((xprt_type == XPRT_UDP) && ((op_ctx->export_perms->options & EXPORT_OPTION_UDP) == 0)) || ((xprt_type == XPRT_TCP) && ((op_ctx->export_perms->options & EXPORT_OPTION_TCP) == 0))) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "NFS4 over %s not allowed on Export_Id %d %s for client %s", xprt_type_to_str(xprt_type), op_ctx->ctx_export->export_id, op_ctx->ctx_export->fullpath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; } /* Check if client is using a privileged port. */ if (((op_ctx->export_perms->options & EXPORT_OPTION_PRIVILEGED_PORT) != 0) && (port >= IPPORT_RESERVED)) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "Non-reserved Port %d is not allowed on Export_Id %d %s for client %s", port, op_ctx->ctx_export->export_id, op_ctx->ctx_export->fullpath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; } /* Test if export allows the authentication provided */ if (export_check_security(req) == false) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "NFS4 auth not allowed on Export_Id %d %s for client %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->fullpath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_WRONGSEC; } /* Get creds */ return nfs_req_creds(req); }
/** * @brief Get numeric credentials from request * * @todo This MUST be refactored to not use TI-RPC private structures. * Instead, export appropriate functions from lib(n)tirpc. * * fills out creds in op_ctx * * @param[in] req Incoming request. * * @return NFS4_OK if successful, NFS4ERR_ACCESS otherwise. * */ nfsstat4 nfs_req_creds(struct svc_req *req) { unsigned int i; const char *auth_label = "UNKNOWN"; gid_t **garray_copy = &op_ctx->caller_garray_copy; #ifdef _HAVE_GSSAPI struct svc_rpc_gss_data *gd = NULL; char principal[MAXNAMLEN + 1]; #endif /* Make sure we clear out all the cred_flags except CREDS_LOADED and * CREDS_ANON. */ op_ctx->cred_flags &= CREDS_LOADED | CREDS_ANON; switch (req->rq_cred.oa_flavor) { case AUTH_NONE: /* Nothing to be done here... */ op_ctx->cred_flags |= CREDS_LOADED | CREDS_ANON; auth_label = "AUTH_NONE"; break; case AUTH_SYS: if ((op_ctx->cred_flags & CREDS_LOADED) == 0) { struct authunix_parms *creds = NULL; /* We map the rq_cred to Authunix_parms */ creds = (struct authunix_parms *) req->rq_clntcred; op_ctx->original_creds.caller_uid = creds->aup_uid; op_ctx->original_creds.caller_gid = creds->aup_gid; op_ctx->original_creds.caller_glen = creds->aup_len; op_ctx->original_creds.caller_garray = creds->aup_gids; op_ctx->cred_flags |= CREDS_LOADED; } /* Copy original_creds creds */ *op_ctx->creds = op_ctx->original_creds; /* Do we trust AUTH_SYS creds for groups or not ? */ if ((op_ctx->export_perms->options & EXPORT_OPTION_MANAGE_GIDS) != 0) { op_ctx->cred_flags |= MANAGED_GIDS; garray_copy = &op_ctx->managed_garray_copy; } auth_label = "AUTH_SYS"; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: if ((op_ctx->cred_flags & CREDS_LOADED) == 0) { /* Get the gss data to process them */ gd = SVCAUTH_PRIVATE(req->rq_auth); memcpy(principal, gd->cname.value, gd->cname.length); principal[gd->cname.length] = 0; LogMidDebug(COMPONENT_DISPATCH, "Mapping RPCSEC_GSS principal %s to uid/gid", principal); /* Convert to uid */ #if _MSPAC_SUPPORT if (!principal2uid(principal, &op_ctx->original_creds.caller_uid, &op_ctx->original_creds.caller_gid, gd)) { #else if (!principal2uid(principal, &op_ctx->original_creds.caller_uid, &op_ctx->original_creds.caller_gid) ) { #endif LogWarn(COMPONENT_IDMAPPER, "Could not map principal %s to uid", principal); /* For compatibility with Linux knfsd, we set * the uid/gid to anonymous when a name->uid * mapping can't be found. */ op_ctx->cred_flags |= CREDS_ANON | CREDS_LOADED; auth_label = "RPCSEC_GSS (no mapping)"; break; } op_ctx->cred_flags |= CREDS_LOADED; } auth_label = "RPCSEC_GSS"; op_ctx->cred_flags |= MANAGED_GIDS; garray_copy = &op_ctx->managed_garray_copy; break; #endif /* _USE_GSSRPC */ default: LogMidDebug(COMPONENT_DISPATCH, "FAILURE: Request xid=%u, has unsupported authentication %d", req->rq_xid, req->rq_cred.oa_flavor); /* Reject the request for weak authentication and * return to worker */ return NFS4ERR_ACCESS; break; } /****************************************************************/ /* Now check for anon creds or id squashing */ /****************************************************************/ if ((op_ctx->cred_flags & CREDS_ANON) != 0 || ((op_ctx->export_perms->options & EXPORT_OPTION_ALL_ANONYMOUS) != 0) || ((op_ctx->export_perms->options & EXPORT_OPTION_ROOT) == 0 && op_ctx->original_creds.caller_uid == 0)) { op_ctx->creds->caller_uid = op_ctx->export_perms->anonymous_uid; op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->creds->caller_glen = 0; LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s creds squashed to uid=%u, gid=%u", auth_label, op_ctx->creds->caller_uid, op_ctx->creds->caller_gid); op_ctx->cred_flags |= UID_SQUASHED | GID_SQUASHED; return NFS4_OK; } /* Now we will use the original_creds uid from original credential */ op_ctx->creds->caller_uid = op_ctx->original_creds.caller_uid; /****************************************************************/ /* Now sqush group or use original_creds gid */ /****************************************************************/ if ((op_ctx->export_perms->options & EXPORT_OPTION_ROOT) == 0 && op_ctx->original_creds.caller_gid == 0) { /* Squash gid */ op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->cred_flags |= GID_SQUASHED; } else { /* Use original_creds gid */ op_ctx->creds->caller_gid = op_ctx->original_creds.caller_gid; } /****************************************************************/ /* Check if we have manage_gids. */ /****************************************************************/ if ((op_ctx->cred_flags & MANAGED_GIDS) != 0) { /* Fetch the group data if required */ if (op_ctx->caller_gdata == NULL && !uid2grp(op_ctx->original_creds.caller_uid, &op_ctx->caller_gdata)) { /** @todo: do we really want to bail here? */ LogCrit(COMPONENT_DISPATCH, "Attempt to fetch managed_gids failed"); return NFS4ERR_ACCESS; } op_ctx->creds->caller_glen = op_ctx->caller_gdata->nbgroups; op_ctx->creds->caller_garray = op_ctx->caller_gdata->groups; } else { /* Use the original_creds group list */ op_ctx->creds->caller_glen = op_ctx->original_creds.caller_glen; op_ctx->creds->caller_garray = op_ctx->original_creds.caller_garray; } /****************************************************************/ /* Check the garray for gid 0 to squash */ /****************************************************************/ /* If no root squashing in caller_garray, return now */ if ((op_ctx->export_perms->options & EXPORT_OPTION_ROOT) != 0 || op_ctx->creds->caller_glen == 0) goto out; for (i = 0; i < op_ctx->creds->caller_glen; i++) { if (op_ctx->creds->caller_garray[i] == 0) { /* Meed to make a copy, or use the old copy */ if ((*garray_copy) == NULL) { /* Make a copy of the active garray */ (*garray_copy) = gsh_malloc(op_ctx->creds->caller_glen * sizeof(gid_t)); memcpy((*garray_copy), op_ctx->creds->caller_garray, op_ctx->creds->caller_glen * sizeof(gid_t)); } /* Now squash the root id. Since the original copy is * always the same, any root ids in it were still in * the same place, so even if using a copy that had a * different anonymous_gid, we're fine. */ (*garray_copy)[i] = op_ctx->export_perms->anonymous_gid; /* Indicate we squashed the caller_garray */ op_ctx->cred_flags |= GARRAY_SQUASHED; } } /* If we squashed the caller_garray, use the squashed copy */ if ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0) op_ctx->creds->caller_garray = *garray_copy; out: LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s creds mapped to uid=%u, gid=%u%s, glen=%d%s", auth_label, op_ctx->creds->caller_uid, op_ctx->creds->caller_gid, (op_ctx->cred_flags & GID_SQUASHED) != 0 ? " (squashed)" : "", op_ctx->creds->caller_glen, (op_ctx->cred_flags & MANAGED_GIDS) != 0 ? ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0 ? " (managed and squashed)" : " (managed)") : ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0 ? " (squashed)" : "")); return NFS4_OK; } /** * @brief Initialize request context and credentials. * */ void init_credentials(void) { memset(op_ctx->creds, 0, sizeof(*op_ctx->creds)); memset(&op_ctx->original_creds, 0, sizeof(op_ctx->original_creds)); op_ctx->creds->caller_uid = op_ctx->export_perms->anonymous_uid; op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->caller_gdata = NULL; op_ctx->caller_garray_copy = NULL; op_ctx->managed_garray_copy = NULL; op_ctx->cred_flags = 0; }