xref: /illumos-gate/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_kdoor.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * "Upcall" glue for the fake (user-mode) smbsrv module.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/kmem.h>
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <sys/cmn_err.h>
35 #include <sys/door.h>
36 #include <smbsrv/smb_kproto.h>
37 #include <smbsrv/smb_door.h>
38 
39 static int smb_kdoor_encode(smb_doorarg_t *);
40 static int smb_kdoor_decode(smb_doorarg_t *);
41 static void smb_kdoor_sethdr(smb_doorarg_t *, uint32_t);
42 static boolean_t smb_kdoor_chkhdr(smb_doorarg_t *, smb_doorhdr_t *);
43 static void smb_kdoor_free(door_arg_t *);
44 
45 void
46 smb_kdoor_init(smb_server_t *sv)
47 {
48 	sv->sv_kdoor_id = -1;
49 	mutex_init(&sv->sv_kdoor_mutex, NULL, MUTEX_DEFAULT, NULL);
50 	cv_init(&sv->sv_kdoor_cv, NULL, CV_DEFAULT, NULL);
51 }
52 
53 void
54 smb_kdoor_fini(smb_server_t *sv)
55 {
56 	smb_kdoor_close(sv);
57 	cv_destroy(&sv->sv_kdoor_cv);
58 	mutex_destroy(&sv->sv_kdoor_mutex);
59 }
60 
61 /*
62  * In the "fake kernen", our "upcalls" don't use the
63  * real door, but just call via a function pointer.
64  * This is where we setup that pointer, which is
65  * fksmbd_door_dispatch()
66  */
67 void
68 fksmb_kdoor_open(smb_server_t *sv, void *varg)
69 {
70 	sv->sv_kdoor_hd = varg;
71 }
72 
73 void
74 smb_kdoor_close(smb_server_t *sv)
75 {
76 	sv->sv_kdoor_hd = NULL;
77 	sv->sv_kdoor_id = -1;
78 }
79 
80 /* ARGSUSED */
81 int
82 smb_kdoor_upcall(smb_server_t *sv, uint32_t cmd,
83     void *req_data, xdrproc_t req_xdr,
84     void *rsp_data, xdrproc_t rsp_xdr)
85 {
86 	smb_doorarg_t	da;
87 	fksmb_kdoor_disp_func_t *func;
88 	int rc;
89 
90 	bzero(&da, sizeof (smb_doorarg_t));
91 	da.da_opcode = cmd;
92 	da.da_opname = smb_doorhdr_opname(cmd);
93 	da.da_req_xdr = req_xdr;
94 	da.da_rsp_xdr = rsp_xdr;
95 	da.da_req_data = req_data;
96 	da.da_rsp_data = rsp_data;
97 
98 	if ((req_data == NULL && req_xdr != NULL) ||
99 	    (rsp_data == NULL && rsp_xdr != NULL)) {
100 		cmn_err(CE_WARN, "smb_kdoor_upcall[%s]: invalid param",
101 		    da.da_opname);
102 		return (-1);
103 	}
104 
105 	/* NB: no ASYNC, nor event stuff */
106 
107 	func = (fksmb_kdoor_disp_func_t *)(sv->sv_kdoor_hd);
108 	if (func == NULL)
109 		return (EFAULT);
110 
111 	if ((rc = smb_kdoor_encode(&da)) != 0)
112 		goto out;
113 
114 	/*
115 	 * The "upcall" (just call via function pointer)
116 	 * i.e. see: fksmbd_door_dispatch()
117 	 */
118 	if ((rc = (*func)(&da)) != 0)
119 		goto out;
120 
121 	rc = smb_kdoor_decode(&da);
122 out:
123 	smb_kdoor_free(&da.da_arg);
124 
125 	return (rc);
126 }
127 
128 /* no smb_kdoor_send, smb_kdoor_receive */
129 /* no smb_kdoor_upcall_private */
130 
131 static int
132 smb_kdoor_encode(smb_doorarg_t *da)
133 {
134 	XDR		xdrs;
135 	char		*buf;
136 	uint32_t	len;
137 
138 	len = xdr_sizeof(smb_doorhdr_xdr, &da->da_hdr);
139 	if (da->da_req_xdr != NULL)
140 		len += xdr_sizeof(da->da_req_xdr, da->da_req_data);
141 
142 	smb_kdoor_sethdr(da, len);
143 
144 	buf = kmem_zalloc(len, KM_SLEEP);
145 	xdrmem_create(&xdrs, buf, len, XDR_ENCODE);
146 
147 	if (!smb_doorhdr_xdr(&xdrs, &da->da_hdr)) {
148 		cmn_err(CE_WARN, "smb_kdoor_encode[%s]: header encode failed",
149 		    da->da_opname);
150 		kmem_free(buf, len);
151 		xdr_destroy(&xdrs);
152 		return (-1);
153 	}
154 
155 	if (da->da_req_xdr != NULL) {
156 		if (!da->da_req_xdr(&xdrs, da->da_req_data)) {
157 			cmn_err(CE_WARN, "smb_kdoor_encode[%s]: encode failed",
158 			    da->da_opname);
159 			kmem_free(buf, len);
160 			xdr_destroy(&xdrs);
161 			return (-1);
162 		}
163 	}
164 
165 	da->da_arg.data_ptr = buf;
166 	da->da_arg.data_size = len;
167 	da->da_arg.desc_ptr = NULL;
168 	da->da_arg.desc_num = 0;
169 	da->da_arg.rbuf = buf;
170 	da->da_arg.rsize = len;
171 
172 	xdr_destroy(&xdrs);
173 	return (0);
174 }
175 
176 /*
177  * Decode the response in rbuf and rsize.
178  */
179 static int
180 smb_kdoor_decode(smb_doorarg_t *da)
181 {
182 	XDR		xdrs;
183 	smb_doorhdr_t	hdr;
184 	char		*rbuf = da->da_arg.rbuf;
185 	uint32_t	rsize = da->da_arg.rsize;
186 
187 	if (rbuf == NULL || rsize == 0) {
188 		cmn_err(CE_WARN, "smb_kdoor_decode[%s]: invalid param",
189 		    da->da_opname);
190 		return (-1);
191 	}
192 
193 	xdrmem_create(&xdrs, rbuf, rsize, XDR_DECODE);
194 
195 	if (!smb_doorhdr_xdr(&xdrs, &hdr)) {
196 		cmn_err(CE_WARN, "smb_kdoor_decode[%s]: header decode failed",
197 		    da->da_opname);
198 		xdr_destroy(&xdrs);
199 		return (-1);
200 	}
201 
202 	if (!smb_kdoor_chkhdr(da, &hdr)) {
203 		xdr_destroy(&xdrs);
204 		return (-1);
205 	}
206 
207 	if (hdr.dh_datalen != 0 && da->da_rsp_xdr != NULL) {
208 		if (!da->da_rsp_xdr(&xdrs, da->da_rsp_data)) {
209 			cmn_err(CE_WARN, "smb_kdoor_decode[%s]: decode failed",
210 			    da->da_opname);
211 			xdr_destroy(&xdrs);
212 			return (-1);
213 		}
214 	}
215 
216 	xdr_destroy(&xdrs);
217 	return (0);
218 }
219 
220 static void
221 smb_kdoor_sethdr(smb_doorarg_t *da, uint32_t datalen)
222 {
223 	smb_doorhdr_t	*hdr = &da->da_hdr;
224 
225 	bzero(hdr, sizeof (smb_doorhdr_t));
226 	hdr->dh_magic = SMB_DOOR_HDR_MAGIC;
227 	hdr->dh_flags = da->da_flags | SMB_DF_FAKE_KERNEL;
228 	hdr->dh_op = da->da_opcode;
229 	/* hdr->dh_txid = 0 (not used) */
230 	hdr->dh_datalen = datalen;
231 	hdr->dh_door_rc = SMB_DOP_NOT_CALLED;
232 }
233 
234 static boolean_t
235 smb_kdoor_chkhdr(smb_doorarg_t *da, smb_doorhdr_t *hdr)
236 {
237 	if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) ||
238 	    (hdr->dh_op != da->da_hdr.dh_op) ||
239 	    (hdr->dh_txid != da->da_hdr.dh_txid)) {
240 		cmn_err(CE_WARN, "smb_kdoor_chkhdr[%s]: invalid header",
241 		    da->da_opname);
242 		return (B_FALSE);
243 	}
244 
245 	switch (hdr->dh_door_rc) {
246 	case SMB_DOP_SUCCESS:
247 		break;
248 
249 	/* SMB_DOP_EMPTYBUF is a "normal" error (silent). */
250 	case SMB_DOP_EMPTYBUF:
251 		return (B_FALSE);
252 
253 	default:
254 		cmn_err(CE_WARN, "smb_kdoor_chkhdr[%s]: call failed: %u",
255 		    da->da_opname, hdr->dh_door_rc);
256 		return (B_FALSE);
257 	}
258 
259 	return (B_TRUE);
260 }
261 
262 /*
263  * Free both the argument and result door buffers regardless of the status
264  * of the up-call.  The doorfs allocates a new buffer if the result buffer
265  * passed by the client is too small.
266  */
267 static void
268 smb_kdoor_free(door_arg_t *arg)
269 {
270 	if (arg->rbuf != NULL && arg->rbuf != arg->data_ptr)
271 		kmem_free(arg->rbuf, arg->rsize);
272 
273 	if (arg->data_ptr != NULL)
274 		kmem_free(arg->data_ptr, arg->data_size);
275 }
276