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
smb_kdoor_init(smb_server_t * sv)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
smb_kdoor_fini(smb_server_t * sv)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
fksmb_kdoor_open(smb_server_t * sv,void * varg)68 fksmb_kdoor_open(smb_server_t *sv, void *varg)
69 {
70 sv->sv_kdoor_hd = varg;
71 }
72
73 void
smb_kdoor_close(smb_server_t * sv)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
smb_kdoor_upcall(smb_server_t * sv,uint32_t cmd,void * req_data,xdrproc_t req_xdr,void * rsp_data,xdrproc_t rsp_xdr)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
smb_kdoor_encode(smb_doorarg_t * da)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
smb_kdoor_decode(smb_doorarg_t * da)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
smb_kdoor_sethdr(smb_doorarg_t * da,uint32_t datalen)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
smb_kdoor_chkhdr(smb_doorarg_t * da,smb_doorhdr_t * hdr)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
smb_kdoor_free(door_arg_t * arg)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