NFS do not find client in NFSv4 pg_authenticate

The information required to find the nfs_client cooresponding to the incoming
back channel request is contained in the NFS layer. Perform minimal checking
in the RPC layer pg_authenticate method, and push more detailed checking into
the NFS layer where the nfs_client can be found.

Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

authored by Andy Adamson and committed by Trond Myklebust 778be232 80c30e8d

+42 -128
+29 -80
fs/nfs/callback.c
··· 135 136 #if defined(CONFIG_NFS_V4_1) 137 /* 138 - * * CB_SEQUENCE operations will fail until the callback sessionid is set. 139 - * */ 140 - int nfs4_set_callback_sessionid(struct nfs_client *clp) 141 - { 142 - struct svc_serv *serv = clp->cl_rpcclient->cl_xprt->bc_serv; 143 - struct nfs4_sessionid *bc_sid; 144 - 145 - if (!serv->sv_bc_xprt) 146 - return -EINVAL; 147 - 148 - /* on success freed in xprt_free */ 149 - bc_sid = kmalloc(sizeof(struct nfs4_sessionid), GFP_KERNEL); 150 - if (!bc_sid) 151 - return -ENOMEM; 152 - memcpy(bc_sid->data, &clp->cl_session->sess_id.data, 153 - NFS4_MAX_SESSIONID_LEN); 154 - spin_lock_bh(&serv->sv_cb_lock); 155 - serv->sv_bc_xprt->xpt_bc_sid = bc_sid; 156 - spin_unlock_bh(&serv->sv_cb_lock); 157 - dprintk("%s set xpt_bc_sid=%u:%u:%u:%u for sv_bc_xprt %p\n", __func__, 158 - ((u32 *)bc_sid->data)[0], ((u32 *)bc_sid->data)[1], 159 - ((u32 *)bc_sid->data)[2], ((u32 *)bc_sid->data)[3], 160 - serv->sv_bc_xprt); 161 - return 0; 162 - } 163 - 164 - /* 165 * The callback service for NFSv4.1 callbacks 166 */ 167 static int ··· 239 struct nfs_callback_data *cb_info) 240 { 241 } 242 - int nfs4_set_callback_sessionid(struct nfs_client *clp) 243 - { 244 - return 0; 245 - } 246 #endif /* CONFIG_NFS_V4_1 */ 247 248 /* ··· 328 mutex_unlock(&nfs_callback_mutex); 329 } 330 331 - static int check_gss_callback_principal(struct nfs_client *clp, 332 - struct svc_rqst *rqstp) 333 { 334 struct rpc_clnt *r = clp->cl_rpcclient; 335 char *p = svc_gss_principal(rqstp); 336 337 /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */ 338 if (clp->cl_minorversion != 0) 339 - return SVC_DROP; 340 /* 341 * It might just be a normal user principal, in which case 342 * userspace won't bother to tell us the name at all. 343 */ 344 if (p == NULL) 345 - return SVC_DENIED; 346 347 /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ 348 349 if (memcmp(p, "nfs@", 4) != 0) 350 - return SVC_DENIED; 351 p += 4; 352 if (strcmp(p, r->cl_server) != 0) 353 - return SVC_DENIED; 354 - return SVC_OK; 355 } 356 357 - /* pg_authenticate method helper */ 358 - static struct nfs_client *nfs_cb_find_client(struct svc_rqst *rqstp) 359 - { 360 - struct nfs4_sessionid *sessionid = bc_xprt_sid(rqstp); 361 - int is_cb_compound = rqstp->rq_proc == CB_COMPOUND ? 1 : 0; 362 - 363 - dprintk("--> %s rq_proc %d\n", __func__, rqstp->rq_proc); 364 - if (svc_is_backchannel(rqstp)) 365 - /* Sessionid (usually) set after CB_NULL ping */ 366 - return nfs4_find_client_sessionid(svc_addr(rqstp), sessionid, 367 - is_cb_compound); 368 - else 369 - /* No callback identifier in pg_authenticate */ 370 - return nfs4_find_client_no_ident(svc_addr(rqstp)); 371 - } 372 - 373 - /* pg_authenticate method for nfsv4 callback threads. */ 374 static int nfs_callback_authenticate(struct svc_rqst *rqstp) 375 { 376 - struct nfs_client *clp; 377 - RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); 378 - int ret = SVC_OK; 379 - 380 - /* Don't talk to strangers */ 381 - clp = nfs_cb_find_client(rqstp); 382 - if (clp == NULL) 383 - return SVC_DROP; 384 - 385 - dprintk("%s: %s NFSv4 callback!\n", __func__, 386 - svc_print_addr(rqstp, buf, sizeof(buf))); 387 - 388 switch (rqstp->rq_authop->flavour) { 389 - case RPC_AUTH_NULL: 390 - if (rqstp->rq_proc != CB_NULL) 391 - ret = SVC_DENIED; 392 - break; 393 - case RPC_AUTH_UNIX: 394 - break; 395 - case RPC_AUTH_GSS: 396 - ret = check_gss_callback_principal(clp, rqstp); 397 - break; 398 - default: 399 - ret = SVC_DENIED; 400 } 401 - nfs_put_client(clp); 402 - return ret; 403 } 404 405 /*
··· 135 136 #if defined(CONFIG_NFS_V4_1) 137 /* 138 * The callback service for NFSv4.1 callbacks 139 */ 140 static int ··· 266 struct nfs_callback_data *cb_info) 267 { 268 } 269 #endif /* CONFIG_NFS_V4_1 */ 270 271 /* ··· 359 mutex_unlock(&nfs_callback_mutex); 360 } 361 362 + /* Boolean check of RPC_AUTH_GSS principal */ 363 + int 364 + check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) 365 { 366 struct rpc_clnt *r = clp->cl_rpcclient; 367 char *p = svc_gss_principal(rqstp); 368 369 + if (rqstp->rq_authop->flavour != RPC_AUTH_GSS) 370 + return 1; 371 + 372 /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */ 373 if (clp->cl_minorversion != 0) 374 + return 0; 375 /* 376 * It might just be a normal user principal, in which case 377 * userspace won't bother to tell us the name at all. 378 */ 379 if (p == NULL) 380 + return 0; 381 382 /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ 383 384 if (memcmp(p, "nfs@", 4) != 0) 385 + return 0; 386 p += 4; 387 if (strcmp(p, r->cl_server) != 0) 388 + return 0; 389 + return 1; 390 } 391 392 + /* 393 + * pg_authenticate method for nfsv4 callback threads. 394 + * 395 + * The authflavor has been negotiated, so an incorrect flavor is a server 396 + * bug. Drop packets with incorrect authflavor. 397 + * 398 + * All other checking done after NFS decoding where the nfs_client can be 399 + * found in nfs4_callback_compound 400 + */ 401 static int nfs_callback_authenticate(struct svc_rqst *rqstp) 402 { 403 switch (rqstp->rq_authop->flavour) { 404 + case RPC_AUTH_NULL: 405 + if (rqstp->rq_proc != CB_NULL) 406 + return SVC_DROP; 407 + break; 408 + case RPC_AUTH_GSS: 409 + /* No RPC_AUTH_GSS support yet in NFSv4.1 */ 410 + if (svc_is_backchannel(rqstp)) 411 + return SVC_DROP; 412 } 413 + return SVC_OK; 414 } 415 416 /*
+2 -2
fs/nfs/callback.h
··· 7 */ 8 #ifndef __LINUX_FS_NFS_CALLBACK_H 9 #define __LINUX_FS_NFS_CALLBACK_H 10 11 #define NFS4_CALLBACK 0x40000000 12 #define NFS4_CALLBACK_XDRSIZE 2048 ··· 38 struct cb_process_state { 39 __be32 drc_status; 40 struct nfs_client *clp; 41 - struct nfs4_sessionid *svc_sid; /* v4.1 callback service sessionid */ 42 }; 43 44 struct cb_compound_hdr_arg { ··· 168 extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); 169 extern void nfs4_cb_take_slot(struct nfs_client *clp); 170 #endif /* CONFIG_NFS_V4_1 */ 171 - 172 extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, 173 struct cb_getattrres *res, 174 struct cb_process_state *cps);
··· 7 */ 8 #ifndef __LINUX_FS_NFS_CALLBACK_H 9 #define __LINUX_FS_NFS_CALLBACK_H 10 + #include <linux/sunrpc/svc.h> 11 12 #define NFS4_CALLBACK 0x40000000 13 #define NFS4_CALLBACK_XDRSIZE 2048 ··· 37 struct cb_process_state { 38 __be32 drc_status; 39 struct nfs_client *clp; 40 }; 41 42 struct cb_compound_hdr_arg { ··· 168 extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); 169 extern void nfs4_cb_take_slot(struct nfs_client *clp); 170 #endif /* CONFIG_NFS_V4_1 */ 171 + extern int check_gss_callback_principal(struct nfs_client *, struct svc_rqst *); 172 extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, 173 struct cb_getattrres *res, 174 struct cb_process_state *cps);
+2 -8
fs/nfs/callback_proc.c
··· 373 { 374 struct nfs_client *clp; 375 int i; 376 - __be32 status; 377 378 cps->clp = NULL; 379 380 - status = htonl(NFS4ERR_BADSESSION); 381 - /* Incoming session must match the callback session */ 382 - if (memcmp(&args->csa_sessionid, cps->svc_sid, NFS4_MAX_SESSIONID_LEN)) 383 - goto out; 384 - 385 - clp = nfs4_find_client_sessionid(args->csa_addr, 386 - &args->csa_sessionid, 1); 387 if (clp == NULL) 388 goto out; 389
··· 373 { 374 struct nfs_client *clp; 375 int i; 376 + __be32 status = htonl(NFS4ERR_BADSESSION); 377 378 cps->clp = NULL; 379 380 + clp = nfs4_find_client_sessionid(args->csa_addr, &args->csa_sessionid); 381 if (clp == NULL) 382 goto out; 383
+2 -3
fs/nfs/callback_xdr.c
··· 794 795 if (hdr_arg.minorversion == 0) { 796 cps.clp = nfs4_find_client_ident(hdr_arg.cb_ident); 797 - if (!cps.clp) 798 return rpc_drop_reply; 799 - } else 800 - cps.svc_sid = bc_xprt_sid(rqstp); 801 802 hdr_res.taglen = hdr_arg.taglen; 803 hdr_res.tag = hdr_arg.tag;
··· 794 795 if (hdr_arg.minorversion == 0) { 796 cps.clp = nfs4_find_client_ident(hdr_arg.cb_ident); 797 + if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) 798 return rpc_drop_reply; 799 + } 800 801 hdr_res.taglen = hdr_arg.taglen; 802 hdr_res.tag = hdr_arg.tag;
+5 -10
fs/nfs/client.c
··· 1206 * For CB_COMPOUND calls, find a client by IP address, protocol version, 1207 * minorversion, and sessionID 1208 * 1209 - * CREATE_SESSION triggers a CB_NULL ping from servers. The callback service 1210 - * sessionid can only be set after the CREATE_SESSION return, so a CB_NULL 1211 - * can arrive before the callback sessionid is set. For CB_NULL calls, 1212 - * find a client by IP address protocol version, and minorversion. 1213 - * 1214 * Returns NULL if no such client 1215 */ 1216 struct nfs_client * 1217 nfs4_find_client_sessionid(const struct sockaddr *addr, 1218 - struct nfs4_sessionid *sid, int is_cb_compound) 1219 { 1220 struct nfs_client *clp; 1221 ··· 1222 if (!nfs4_has_session(clp)) 1223 continue; 1224 1225 - /* Match sessionid unless cb_null call*/ 1226 - if (is_cb_compound && (memcmp(clp->cl_session->sess_id.data, 1227 - sid->data, NFS4_MAX_SESSIONID_LEN) != 0)) 1228 continue; 1229 1230 atomic_inc(&clp->cl_count); ··· 1239 1240 struct nfs_client * 1241 nfs4_find_client_sessionid(const struct sockaddr *addr, 1242 - struct nfs4_sessionid *sid, int is_cb_compound) 1243 { 1244 return NULL; 1245 }
··· 1206 * For CB_COMPOUND calls, find a client by IP address, protocol version, 1207 * minorversion, and sessionID 1208 * 1209 * Returns NULL if no such client 1210 */ 1211 struct nfs_client * 1212 nfs4_find_client_sessionid(const struct sockaddr *addr, 1213 + struct nfs4_sessionid *sid) 1214 { 1215 struct nfs_client *clp; 1216 ··· 1227 if (!nfs4_has_session(clp)) 1228 continue; 1229 1230 + /* Match sessionid*/ 1231 + if (memcmp(clp->cl_session->sess_id.data, 1232 + sid->data, NFS4_MAX_SESSIONID_LEN) != 0) 1233 continue; 1234 1235 atomic_inc(&clp->cl_count); ··· 1244 1245 struct nfs_client * 1246 nfs4_find_client_sessionid(const struct sockaddr *addr, 1247 + struct nfs4_sessionid *sid) 1248 { 1249 return NULL; 1250 }
+1 -2
fs/nfs/internal.h
··· 133 extern struct nfs_client *nfs4_find_client_no_ident(const struct sockaddr *); 134 extern struct nfs_client *nfs4_find_client_ident(int); 135 extern struct nfs_client * 136 - nfs4_find_client_sessionid(const struct sockaddr *, struct nfs4_sessionid *, 137 - int); 138 extern struct nfs_server *nfs_create_server( 139 const struct nfs_parsed_mount_data *, 140 struct nfs_fh *);
··· 133 extern struct nfs_client *nfs4_find_client_no_ident(const struct sockaddr *); 134 extern struct nfs_client *nfs4_find_client_ident(int); 135 extern struct nfs_client * 136 + nfs4_find_client_sessionid(const struct sockaddr *, struct nfs4_sessionid *); 137 extern struct nfs_server *nfs_create_server( 138 const struct nfs_parsed_mount_data *, 139 struct nfs_fh *);
-6
fs/nfs/nfs4state.c
··· 232 status = nfs4_proc_create_session(clp); 233 if (status != 0) 234 goto out; 235 - status = nfs4_set_callback_sessionid(clp); 236 - if (status != 0) { 237 - printk(KERN_WARNING "Sessionid not set. No callback service\n"); 238 - nfs_callback_down(1); 239 - status = 0; 240 - } 241 nfs41_setup_state_renewal(clp); 242 nfs_mark_client_ready(clp, NFS_CS_READY); 243 out:
··· 232 status = nfs4_proc_create_session(clp); 233 if (status != 0) 234 goto out; 235 nfs41_setup_state_renewal(clp); 236 nfs_mark_client_ready(clp, NFS_CS_READY); 237 out:
-13
include/linux/sunrpc/bc_xprt.h
··· 47 return 1; 48 return 0; 49 } 50 - static inline struct nfs4_sessionid *bc_xprt_sid(struct svc_rqst *rqstp) 51 - { 52 - if (svc_is_backchannel(rqstp)) 53 - return (struct nfs4_sessionid *) 54 - rqstp->rq_server->sv_bc_xprt->xpt_bc_sid; 55 - return NULL; 56 - } 57 - 58 #else /* CONFIG_NFS_V4_1 */ 59 static inline int xprt_setup_backchannel(struct rpc_xprt *xprt, 60 unsigned int min_reqs) ··· 57 static inline int svc_is_backchannel(const struct svc_rqst *rqstp) 58 { 59 return 0; 60 - } 61 - 62 - static inline struct nfs4_sessionid *bc_xprt_sid(struct svc_rqst *rqstp) 63 - { 64 - return NULL; 65 } 66 67 static inline void xprt_free_bc_request(struct rpc_rqst *req)
··· 47 return 1; 48 return 0; 49 } 50 #else /* CONFIG_NFS_V4_1 */ 51 static inline int xprt_setup_backchannel(struct rpc_xprt *xprt, 52 unsigned int min_reqs) ··· 65 static inline int svc_is_backchannel(const struct svc_rqst *rqstp) 66 { 67 return 0; 68 } 69 70 static inline void xprt_free_bc_request(struct rpc_rqst *req)
-1
include/linux/sunrpc/svc_xprt.h
··· 77 size_t xpt_remotelen; /* length of address */ 78 struct rpc_wait_queue xpt_bc_pending; /* backchannel wait queue */ 79 struct list_head xpt_users; /* callbacks on free */ 80 - void *xpt_bc_sid; /* back channel session ID */ 81 82 struct net *xpt_net; 83 struct rpc_xprt *xpt_bc_xprt; /* NFSv4.1 backchannel */
··· 77 size_t xpt_remotelen; /* length of address */ 78 struct rpc_wait_queue xpt_bc_pending; /* backchannel wait queue */ 79 struct list_head xpt_users; /* callbacks on free */ 80 81 struct net *xpt_net; 82 struct rpc_xprt *xpt_bc_xprt; /* NFSv4.1 backchannel */
+1 -3
net/sunrpc/svcsock.c
··· 1609 */ 1610 static void svc_bc_sock_free(struct svc_xprt *xprt) 1611 { 1612 - if (xprt) { 1613 - kfree(xprt->xpt_bc_sid); 1614 kfree(container_of(xprt, struct svc_sock, sk_xprt)); 1615 - } 1616 } 1617 #endif /* CONFIG_NFS_V4_1 */
··· 1609 */ 1610 static void svc_bc_sock_free(struct svc_xprt *xprt) 1611 { 1612 + if (xprt) 1613 kfree(container_of(xprt, struct svc_sock, sk_xprt)); 1614 } 1615 #endif /* CONFIG_NFS_V4_1 */