xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_kshare.c (revision 56f33205c9ed776c3c909e07d52e94610a675740)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Kernel door client for LanMan share management.
28  */
29 
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/cmn_err.h>
33 #include <sys/door.h>
34 #include <smbsrv/lmerr.h>
35 #include <smbsrv/smb_share.h>
36 #include <smbsrv/smb_common_door.h>
37 #include <smbsrv/smbinfo.h>
38 
39 static int smb_kshare_chk_dsrv_status(int, smb_dr_ctx_t *);
40 
41 /*
42  * smb_kshare_init
43  *
44  * This function is not MultiThread safe. The caller has to make sure only one
45  * thread calls this function.
46  */
47 door_handle_t
48 smb_kshare_init(int door_id)
49 {
50 	return (door_ki_lookup(door_id));
51 }
52 
53 /*
54  * smb_kshare_fini
55  *
56  * This function is not MultiThread safe. The caller has to make sure only one
57  * thread calls this function.
58  */
59 void
60 smb_kshare_fini(door_handle_t dhdl)
61 {
62 	if (dhdl)
63 		door_ki_rele(dhdl);
64 }
65 
66 uint32_t
67 smb_kshare_getinfo(door_handle_t dhdl, char *share_name, smb_share_t *si,
68     smb_inaddr_t *ipaddr)
69 {
70 	door_arg_t arg;
71 	char *buf;
72 	unsigned int used;
73 	smb_dr_ctx_t *dec_ctx;
74 	smb_dr_ctx_t *enc_ctx;
75 	uint32_t rc;
76 	int opcode = SMB_SHROP_GETINFO;
77 
78 	buf = kmem_alloc(SMB_SHARE_DSIZE, KM_SLEEP);
79 
80 	enc_ctx = smb_dr_encode_start(buf, SMB_SHARE_DSIZE);
81 	smb_dr_put_uint32(enc_ctx, opcode);
82 	smb_dr_put_string(enc_ctx, share_name);
83 	smb_dr_put_buf(enc_ctx, (uchar_t *)ipaddr, sizeof (smb_inaddr_t));
84 
85 	if (smb_dr_encode_finish(enc_ctx, &used) != 0) {
86 		kmem_free(buf, SMB_SHARE_DSIZE);
87 		return (NERR_InternalError);
88 	}
89 
90 	arg.data_ptr = buf;
91 	arg.data_size = used;
92 	arg.desc_ptr = NULL;
93 	arg.desc_num = 0;
94 	arg.rbuf = buf;
95 	arg.rsize = SMB_SHARE_DSIZE;
96 
97 	if (door_ki_upcall_limited(dhdl, &arg, NULL, SIZE_MAX, 0) != 0) {
98 		kmem_free(buf, SMB_SHARE_DSIZE);
99 		return (NERR_InternalError);
100 	}
101 
102 	dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
103 	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
104 		kmem_free(buf, SMB_SHARE_DSIZE);
105 		return (NERR_InternalError);
106 	}
107 
108 	rc = smb_dr_get_uint32(dec_ctx);
109 	smb_dr_get_share(dec_ctx, si);
110 	if (smb_dr_decode_finish(dec_ctx) != 0)
111 		rc = NERR_InternalError;
112 
113 	kmem_free(buf, SMB_SHARE_DSIZE);
114 	return (rc);
115 }
116 
117 uint32_t
118 smb_kshare_enum(door_handle_t dhdl, smb_enumshare_info_t *enuminfo)
119 {
120 	door_arg_t arg;
121 	char *door_buf;
122 	int door_bufsz;
123 	unsigned int used;
124 	smb_dr_ctx_t *dec_ctx;
125 	smb_dr_ctx_t *enc_ctx;
126 	uint32_t rc;
127 	int opcode = SMB_SHROP_ENUM;
128 
129 	enuminfo->es_ntotal = enuminfo->es_nsent = 0;
130 
131 	door_bufsz = enuminfo->es_bufsize + strlen(enuminfo->es_username)
132 	    + sizeof (smb_enumshare_info_t);
133 	door_buf = kmem_alloc(door_bufsz, KM_SLEEP);
134 
135 	enc_ctx = smb_dr_encode_start(door_buf, door_bufsz);
136 	smb_dr_put_uint32(enc_ctx, opcode);
137 	smb_dr_put_ushort(enc_ctx, enuminfo->es_bufsize);
138 	smb_dr_put_string(enc_ctx, enuminfo->es_username);
139 
140 	if (smb_dr_encode_finish(enc_ctx, &used) != 0) {
141 		kmem_free(door_buf, door_bufsz);
142 		return (NERR_InternalError);
143 	}
144 
145 	arg.data_ptr = door_buf;
146 	arg.data_size = used;
147 	arg.desc_ptr = NULL;
148 	arg.desc_num = 0;
149 	arg.rbuf = door_buf;
150 	arg.rsize = door_bufsz;
151 
152 	if (door_ki_upcall_limited(dhdl, &arg, NULL, SIZE_MAX, 0) != 0) {
153 		kmem_free(door_buf, door_bufsz);
154 		return (NERR_InternalError);
155 	}
156 
157 	dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
158 	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
159 		kmem_free(door_buf, door_bufsz);
160 		return (NERR_InternalError);
161 	}
162 
163 	rc = smb_dr_get_uint32(dec_ctx);
164 	if (rc == NERR_Success) {
165 		enuminfo->es_ntotal = smb_dr_get_ushort(dec_ctx);
166 		enuminfo->es_nsent = smb_dr_get_ushort(dec_ctx);
167 		enuminfo->es_datasize = smb_dr_get_ushort(dec_ctx);
168 		(void) smb_dr_get_buf(dec_ctx,
169 		    (unsigned char *)enuminfo->es_buf,
170 		    enuminfo->es_bufsize);
171 	}
172 
173 	if (smb_dr_decode_finish(dec_ctx) != 0)
174 		rc = NERR_InternalError;
175 
176 	kmem_free(door_buf, door_bufsz);
177 	return (rc);
178 }
179 
180 /*
181  * Executes map and unmap command for shares.
182  */
183 uint32_t
184 smb_kshare_exec(door_handle_t dhdl, char *sharename, smb_execsub_info_t *subs,
185     int exec_type)
186 {
187 	door_arg_t arg;
188 	char *buf;
189 	int bufsz;
190 	unsigned int used;
191 	smb_dr_ctx_t *dec_ctx;
192 	smb_dr_ctx_t *enc_ctx;
193 	uint32_t rc;
194 	int opcode = SMB_SHROP_EXEC;
195 
196 	bufsz = (2 * sizeof (int)) + strlen(sharename) + strlen(subs->e_winname)
197 	    + strlen(subs->e_userdom) + strlen(subs->e_cli_netbiosname) +
198 	    (2 * sizeof (smb_inaddr_t)) + sizeof (uid_t) +
199 	    sizeof (smb_execsub_info_t);
200 
201 	buf = kmem_alloc(bufsz, KM_SLEEP);
202 
203 	enc_ctx = smb_dr_encode_start(buf, bufsz);
204 	smb_dr_put_uint32(enc_ctx, opcode);
205 	smb_dr_put_string(enc_ctx, sharename);
206 	smb_dr_put_string(enc_ctx, subs->e_winname);
207 	smb_dr_put_string(enc_ctx, subs->e_userdom);
208 	smb_dr_put_buf(enc_ctx, (uchar_t *)&subs->e_srv_ipaddr,
209 	    sizeof (smb_inaddr_t));
210 	smb_dr_put_buf(enc_ctx, (uchar_t *)&subs->e_cli_ipaddr,
211 	    sizeof (smb_inaddr_t));
212 	smb_dr_put_string(enc_ctx, subs->e_cli_netbiosname);
213 	smb_dr_put_int32(enc_ctx, subs->e_uid);
214 	smb_dr_put_int32(enc_ctx, exec_type);
215 
216 	if (smb_dr_encode_finish(enc_ctx, &used) != 0) {
217 		kmem_free(buf, bufsz);
218 		return (NERR_InternalError);
219 	}
220 
221 	arg.data_ptr = buf;
222 	arg.data_size = used;
223 	arg.desc_ptr = NULL;
224 	arg.desc_num = 0;
225 	arg.rbuf = buf;
226 	arg.rsize = bufsz;
227 
228 	if (door_ki_upcall_limited(dhdl, &arg, NULL, SIZE_MAX, 0) != 0) {
229 		kmem_free(buf, bufsz);
230 		return (NERR_InternalError);
231 	}
232 
233 	dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
234 	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
235 		kmem_free(buf, bufsz);
236 		return (NERR_InternalError);
237 	}
238 
239 	rc = smb_dr_get_uint32(dec_ctx);
240 	if (smb_dr_decode_finish(dec_ctx) != 0)
241 		rc = NERR_InternalError;
242 
243 	kmem_free(buf, bufsz);
244 	return (rc);
245 }
246 
247 /*
248  * This is a special interface that will be utilized by ZFS to cause
249  * a share to be added/removed
250  *
251  * arg is either a smb_share_t or share_name from userspace.
252  * It will need to be copied into the kernel.   It is smb_share_t
253  * for add operations and share_name for delete operations.
254  */
255 int
256 smb_kshare_upcall(door_handle_t dhdl, void *arg, boolean_t add_share)
257 {
258 	door_arg_t	doorarg = { 0 };
259 	char		*buf = NULL;
260 	char		*str = NULL;
261 	int		error;
262 	int		rc;
263 	unsigned int	used;
264 	smb_dr_ctx_t	*dec_ctx;
265 	smb_dr_ctx_t	*enc_ctx;
266 	smb_share_t	*lmshare = NULL;
267 	int		opcode;
268 
269 	opcode = (add_share) ? SMB_SHROP_ADD : SMB_SHROP_DELETE;
270 
271 	buf = kmem_alloc(SMB_SHARE_DSIZE, KM_SLEEP);
272 	enc_ctx = smb_dr_encode_start(buf, SMB_SHARE_DSIZE);
273 	smb_dr_put_uint32(enc_ctx, opcode);
274 
275 	switch (opcode) {
276 	case SMB_SHROP_ADD:
277 		lmshare = kmem_alloc(sizeof (smb_share_t), KM_SLEEP);
278 		if (error = xcopyin(arg, lmshare, sizeof (smb_share_t))) {
279 			kmem_free(lmshare, sizeof (smb_share_t));
280 			kmem_free(buf, SMB_SHARE_DSIZE);
281 			return (error);
282 		}
283 		smb_dr_put_share(enc_ctx, lmshare);
284 		break;
285 
286 	case SMB_SHROP_DELETE:
287 		str = kmem_alloc(MAXPATHLEN, KM_SLEEP);
288 		if (error = copyinstr(arg, str, MAXPATHLEN, NULL)) {
289 			kmem_free(str, MAXPATHLEN);
290 			kmem_free(buf, SMB_SHARE_DSIZE);
291 			return (error);
292 		}
293 		smb_dr_put_string(enc_ctx, str);
294 		kmem_free(str, MAXPATHLEN);
295 		break;
296 	}
297 
298 	if ((error = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
299 		kmem_free(buf, SMB_SHARE_DSIZE);
300 		if (lmshare)
301 			kmem_free(lmshare, sizeof (smb_share_t));
302 		return (NERR_InternalError);
303 	}
304 
305 	doorarg.data_ptr = buf;
306 	doorarg.data_size = used;
307 	doorarg.rbuf = buf;
308 	doorarg.rsize = SMB_SHARE_DSIZE;
309 
310 	error = door_ki_upcall_limited(dhdl, &doorarg, NULL, SIZE_MAX, 0);
311 
312 	if (error) {
313 		kmem_free(buf, SMB_SHARE_DSIZE);
314 		if (lmshare)
315 			kmem_free(lmshare, sizeof (smb_share_t));
316 		return (error);
317 	}
318 
319 	dec_ctx = smb_dr_decode_start(doorarg.data_ptr, doorarg.data_size);
320 	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
321 		kmem_free(buf, SMB_SHARE_DSIZE);
322 		if (lmshare)
323 			kmem_free(lmshare, sizeof (smb_share_t));
324 		return (NERR_InternalError);
325 	}
326 
327 	rc = smb_dr_get_uint32(dec_ctx);
328 	if (opcode == SMB_SHROP_ADD)
329 		smb_dr_get_share(dec_ctx, lmshare);
330 
331 	if (smb_dr_decode_finish(dec_ctx))
332 		rc = NERR_InternalError;
333 
334 	kmem_free(buf, SMB_SHARE_DSIZE);
335 	if (lmshare)
336 		kmem_free(lmshare, sizeof (smb_share_t));
337 
338 	return ((rc == NERR_DuplicateShare && add_share) ? 0 : rc);
339 }
340 
341 /*
342  * Return 0 upon success. Otherwise > 0
343  */
344 static int
345 smb_kshare_chk_dsrv_status(int opcode, smb_dr_ctx_t *dec_ctx)
346 {
347 	int status = smb_dr_get_int32(dec_ctx);
348 	int err;
349 
350 	switch (status) {
351 	case SMB_SHARE_DSUCCESS:
352 		return (0);
353 
354 	case SMB_SHARE_DERROR:
355 		err = smb_dr_get_uint32(dec_ctx);
356 		cmn_err(CE_WARN, "%d: Encountered door server error %d",
357 		    opcode, err);
358 		(void) smb_dr_decode_finish(dec_ctx);
359 		return (err);
360 	}
361 
362 	ASSERT(0);
363 	return (EINVAL);
364 }
365