diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c index 19896a59718..daf8082fa1c 100644 --- a/sys/fs/nfs/nfs_commonkrpc.c +++ b/sys/fs/nfs/nfs_commonkrpc.c @@ -281,8 +281,12 @@ newnfs_connect(struct nfsmount *nmp, struct nfssockreq *nrp, CLNT_CONTROL(client, CLSET_INTERRUPTIBLE, &one); if ((nmp->nm_flag & NFSMNT_RESVPORT)) CLNT_CONTROL(client, CLSET_PRIVPORT, &one); - if (NFSHASTLS(nmp)) + if (NFSHASTLS(nmp)) { CLNT_CONTROL(client, CLSET_TLS, &one); + if (nmp->nm_tlscertname != NULL) + CLNT_CONTROL(client, CLSET_TLSCERTNAME, + nmp->nm_tlscertname); + } if (NFSHASSOFT(nmp)) { if (nmp->nm_sotype == SOCK_DGRAM) /* diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c index 8b059ef1be9..365b1c387cc 100644 --- a/sys/fs/nfsclient/nfs_clvfsops.c +++ b/sys/fs/nfsclient/nfs_clvfsops.c @@ -119,7 +119,7 @@ static void nfs_decode_args(struct mount *mp, struct nfsmount *nmp, static int mountnfs(struct nfs_args *, struct mount *, struct sockaddr *, char *, u_char *, int, u_char *, int, u_char *, int, struct vnode **, struct ucred *, - struct thread *, int, int, int, uint32_t); + struct thread *, int, int, int, uint32_t, char *); static void nfs_getnlminfo(struct vnode *, uint8_t *, size_t *, struct sockaddr_storage *, int *, off_t *, struct timeval *); @@ -545,7 +545,7 @@ nfs_mountdiskless(char *path, nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen, NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO, - NFS_DEFAULT_NEGNAMETIMEO, 0, 0)) != 0) { + NFS_DEFAULT_NEGNAMETIMEO, 0, 0, NULL)) != 0) { printf("nfs_mountroot: mount %s on /: %d\n", path, error); return (error); } @@ -747,7 +747,7 @@ static const char *nfs_opts[] = { "from", "nfs_args", "resvport", "readahead", "hostname", "timeo", "timeout", "addr", "fh", "nfsv3", "sec", "principal", "nfsv4", "gssname", "allgssname", "dirpath", "minorversion", "nametimeo", "negnametimeo", "nocto", "noncontigwr", - "pnfs", "wcommitsize", "oneopenown", "tls", + "pnfs", "wcommitsize", "oneopenown", "tls", "tlscertname", NULL }; /* @@ -891,7 +891,7 @@ nfs_mount(struct mount *mp) struct thread *td; char *hst; u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100]; - char *cp, *opt, *name, *secname; + char *cp, *opt, *name, *secname, *tlscertname; int nametimeo = NFS_DEFAULT_NAMETIMEO; int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO; int minvers = 0; @@ -903,6 +903,7 @@ nfs_mount(struct mount *mp) has_nfs_args_opt = 0; has_nfs_from_opt = 0; newflag = 0; + tlscertname = NULL; hst = malloc(MNAMELEN, M_TEMP, M_WAITOK); if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) { error = EINVAL; @@ -988,6 +989,22 @@ nfs_mount(struct mount *mp) args.flags |= NFSMNT_ONEOPENOWN; if (vfs_getopt(mp->mnt_optnew, "tls", NULL, NULL) == 0) newflag |= NFSMNT_TLS; + if (vfs_getopt(mp->mnt_optnew, "tlscertname", (void **)&opt, &len) == + 0) { + /* + * tlscertname with "key.pem" appended to it forms a file + * name. As such, the maximum allowable strlen(tlscertname) is + * NAME_MAX - 7. However, "len" includes the nul termination + * byte so it can be up to NAME_MAX - 6. + */ + if (opt == NULL || len <= 1 || len > NAME_MAX - 6) { + vfs_mount_error(mp, "invalid tlscertname"); + error = EINVAL; + goto out; + } + tlscertname = malloc(len, M_NEWNFSMNT, M_WAITOK); + strlcpy(tlscertname, opt, len); + } if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) { if (opt == NULL) { vfs_mount_error(mp, "illegal readdirsize"); @@ -1342,7 +1359,7 @@ nfs_mount(struct mount *mp) args.fh = nfh; error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath, dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td, - nametimeo, negnametimeo, minvers, newflag); + nametimeo, negnametimeo, minvers, newflag, tlscertname); out: if (!error) { MNT_ILOCK(mp); @@ -1390,7 +1407,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen, u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp, struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo, - int minvers, uint32_t newflag) + int minvers, uint32_t newflag, char *tlscertname) { struct nfsmount *nmp; struct nfsnode *np; @@ -1410,6 +1427,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, nmp = VFSTONFS(mp); printf("%s: MNT_UPDATE is no longer handled here\n", __func__); free(nam, M_SONAME); + free(tlscertname, M_NEWNFSMNT); return (0); } else { /* NFS-over-TLS requires that rpctls be functioning. */ @@ -1423,12 +1441,14 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, #endif if (error != 0) { free(nam, M_SONAME); + free(tlscertname, M_NEWNFSMNT); return (error); } } nmp = malloc(sizeof (struct nfsmount) + krbnamelen + dirlen + srvkrbnamelen + 2, M_NEWNFSMNT, M_WAITOK | M_ZERO); + nmp->nm_tlscertname = tlscertname; nmp->nm_newflag = newflag; TAILQ_INIT(&nmp->nm_bufq); TAILQ_INIT(&nmp->nm_sess); @@ -1681,6 +1701,7 @@ bad: newnfs_disconnect(dsp->nfsclds_sockp); nfscl_freenfsclds(dsp); } + free(nmp->nm_tlscertname, M_NEWNFSMNT); free(nmp, M_NEWNFSMNT); free(nam, M_SONAME); return (error); @@ -1776,6 +1797,7 @@ nfs_unmount(struct mount *mp, int mntflags) newnfs_disconnect(dsp->nfsclds_sockp); nfscl_freenfsclds(dsp); } + free(nmp->nm_tlscertname, M_NEWNFSMNT); free(nmp, M_NEWNFSMNT); out: return (error); diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h index 063926eceaf..57adcd8f2fc 100644 --- a/sys/fs/nfsclient/nfsmount.h +++ b/sys/fs/nfsclient/nfsmount.h @@ -76,6 +76,7 @@ struct nfsmount { /* Newnfs additions */ TAILQ_HEAD(, nfsclds) nm_sess; /* Session(s) for NFSv4.1. */ struct nfsclclient *nm_clp; + char *nm_tlscertname; /* TLS certificate file name */ uid_t nm_uid; /* Uid for SetClientID etc. */ u_int64_t nm_clval; /* identifies which clientid */ u_int64_t nm_fsid[2]; /* NFSv4 fsid */ diff --git a/sys/rpc/clnt.h b/sys/rpc/clnt.h index 23c92103edf..f4cc78b1c3b 100644 --- a/sys/rpc/clnt.h +++ b/sys/rpc/clnt.h @@ -359,6 +359,7 @@ enum clnt_stat clnt_call_private(CLIENT *, struct rpc_callextra *, rpcproc_t, #define CLSET_BACKCHANNEL 29 /* set backchannel for socket */ #define CLSET_TLS 30 /* set TLS for socket */ #define CLSET_BLOCKRCV 31 /* Temporarily block reception */ +#define CLSET_TLSCERTNAME 32 /* TLS certificate file name */ #endif diff --git a/sys/rpc/clnt_rc.c b/sys/rpc/clnt_rc.c index 730001723e9..8c204989d0e 100644 --- a/sys/rpc/clnt_rc.c +++ b/sys/rpc/clnt_rc.c @@ -110,6 +110,7 @@ clnt_reconnect_create( rc->rc_ucred = crdup(curthread->td_ucred); rc->rc_client = NULL; rc->rc_tls = false; + rc->rc_tlscertname = NULL; cl->cl_refs = 1; cl->cl_ops = &clnt_reconnect_ops; @@ -198,7 +199,8 @@ clnt_reconnect_connect(CLIENT *cl) (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers, rc->rc_sendsz, rc->rc_recvsz, rc->rc_intr); if (rc->rc_tls && newclient != NULL) { - stat = rpctls_connect(newclient, so, ssl, &reterr); + stat = rpctls_connect(newclient, rc->rc_tlscertname, so, + ssl, &reterr); if (stat != RPC_SUCCESS || reterr != RPCTLSERR_OK) { if (stat == RPC_SUCCESS) stat = RPC_FAILED; @@ -405,6 +407,7 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info) { struct rc_data *rc = (struct rc_data *)cl->cl_private; SVCXPRT *xprt; + size_t slen; if (info == NULL) { return (FALSE); @@ -496,6 +499,20 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info) rc->rc_tls = true; break; + case CLSET_TLSCERTNAME: + slen = strlen(info) + 1; + /* + * tlscertname with "key.pem" appended to it forms a file + * name. As such, the maximum allowable strlen(info) is + * NAME_MAX - 7. However, "slen" includes the nul termination + * byte so it can be up to NAME_MAX - 6. + */ + if (slen <= 1 || slen > NAME_MAX - 6) + return (FALSE); + rc->rc_tlscertname = mem_alloc(slen); + strlcpy(rc->rc_tlscertname, info, slen); + break; + default: return (FALSE); } @@ -543,6 +560,7 @@ clnt_reconnect_destroy(CLIENT *cl) } crfree(rc->rc_ucred); mtx_destroy(&rc->rc_lock); + mem_free(rc->rc_tlscertname, 0); /* 0 ok, since arg. ignored. */ mem_free(rc, sizeof(*rc)); mem_free(cl, sizeof (CLIENT)); } diff --git a/sys/rpc/krpc.h b/sys/rpc/krpc.h index 53d46deddf6..77facdcf16c 100644 --- a/sys/rpc/krpc.h +++ b/sys/rpc/krpc.h @@ -80,6 +80,7 @@ struct rc_data { struct rpc_err rc_err; void *rc_backchannel; bool rc_tls; /* Enable TLS on connection */ + char *rc_tlscertname; }; /* Bits for ct_rcvstate. */ diff --git a/sys/rpc/rpcsec_tls.h b/sys/rpc/rpcsec_tls.h index a33feff17c0..49a7e71b751 100644 --- a/sys/rpc/rpcsec_tls.h +++ b/sys/rpc/rpcsec_tls.h @@ -58,8 +58,8 @@ int rpctls_syscall(int, const char *); #ifdef _KERNEL /* Functions that perform upcalls to the rpctlsd daemon. */ -enum clnt_stat rpctls_connect(CLIENT *newclient, struct socket *so, - uint64_t *sslp, uint32_t *reterr); +enum clnt_stat rpctls_connect(CLIENT *newclient, char *certname, + struct socket *so, uint64_t *sslp, uint32_t *reterr); enum clnt_stat rpctls_cl_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl, uint32_t *reterr); enum clnt_stat rpctls_srv_handlerecord(uint64_t sec, uint64_t usec, diff --git a/sys/rpc/rpcsec_tls/rpctls_impl.c b/sys/rpc/rpcsec_tls/rpctls_impl.c index c5f3ce5d46a..638f27eaf35 100644 --- a/sys/rpc/rpcsec_tls/rpctls_impl.c +++ b/sys/rpc/rpcsec_tls/rpctls_impl.c @@ -356,9 +356,10 @@ rpctls_server_client(void) /* Do an upcall for a new socket connect using TLS. */ enum clnt_stat -rpctls_connect(CLIENT *newclient, struct socket *so, uint64_t *sslp, - uint32_t *reterr) +rpctls_connect(CLIENT *newclient, char *certname, struct socket *so, + uint64_t *sslp, uint32_t *reterr) { + struct rpctlscd_connect_arg arg; struct rpctlscd_connect_res res; struct rpc_callextra ext; struct timeval utimeout; @@ -399,7 +400,12 @@ rpctls_connect(CLIENT *newclient, struct socket *so, uint64_t *sslp, CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &val); /* Do the connect handshake upcall. */ - stat = rpctlscd_connect_1(NULL, &res, cl); + if (certname != NULL) { + arg.certname.certname_len = strlen(certname); + arg.certname.certname_val = certname; + } else + arg.certname.certname_len = 0; + stat = rpctlscd_connect_1(&arg, &res, cl); if (stat == RPC_SUCCESS) { *reterr = res.reterr; if (res.reterr == 0) { diff --git a/sys/rpc/rpcsec_tls/rpctlscd.x b/sys/rpc/rpcsec_tls/rpctlscd.x index d5c4d61a43a..1ae53d7b8d1 100644 --- a/sys/rpc/rpcsec_tls/rpctlscd.x +++ b/sys/rpc/rpcsec_tls/rpctlscd.x @@ -29,6 +29,10 @@ /* $FreeBSD$ */ +struct rpctlscd_connect_arg { + char certname<>; +}; + struct rpctlscd_connect_res { uint32_t reterr; uint64_t sec; @@ -61,7 +65,7 @@ program RPCTLSCD { void RPCTLSCD_NULL(void) = 0; rpctlscd_connect_res - RPCTLSCD_CONNECT(void) = 1; + RPCTLSCD_CONNECT(rpctlscd_connect_arg) = 1; rpctlscd_handlerecord_res RPCTLSCD_HANDLERECORD(rpctlscd_handlerecord_arg) = 2;