xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_share_doorsvc.c (revision 968633ad8faee931821fd6b656eb0d96d4b186c0)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"@(#)smbd_share_doorsvc.c	1.6	08/08/05 SMI"
27 
28 /*
29  * LanMan share door server
30  */
31 
32 #include <door.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <syslog.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <pthread.h>
42 
43 #include <smbsrv/libsmb.h>
44 
45 #include <smbsrv/smb_share.h>
46 #include <smbsrv/smbinfo.h>
47 
48 #define	SMB_SHARE_DSRV_VERSION	1
49 #define	SMB_SHARE_DSRV_COOKIE	((void*)(0xdeadbeef^SMB_SHARE_DSRV_VERSION))
50 
51 static int smb_share_dsrv_fd = -1;
52 static pthread_mutex_t smb_share_dsrv_mtx = PTHREAD_MUTEX_INITIALIZER;
53 
54 static void smb_share_dsrv_dispatch(void *, char *, size_t, door_desc_t *,
55     uint_t);
56 static int smb_share_dsrv_enum(smb_enumshare_info_t *esi);
57 
58 /*
59  * smb_share_dsrv_start
60  *
61  * Start the LanMan share door service.
62  * Returns 0 on success. Otherwise, -1.
63  */
64 int
65 smb_share_dsrv_start(void)
66 {
67 	int	newfd;
68 
69 	(void) pthread_mutex_lock(&smb_share_dsrv_mtx);
70 
71 	if (smb_share_dsrv_fd != -1) {
72 		syslog(LOG_ERR, "smb_share_dsrv_start: duplicate");
73 		(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
74 		return (smb_share_dsrv_fd);
75 	}
76 
77 	if ((smb_share_dsrv_fd = door_create(smb_share_dsrv_dispatch,
78 	    SMB_SHARE_DSRV_COOKIE, (DOOR_UNREF | DOOR_REFUSE_DESC))) < 0) {
79 		syslog(LOG_ERR, "smb_share_dsrv_start: door_create: %s",
80 		    strerror(errno));
81 		(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
82 		return (-1);
83 	}
84 
85 	(void) unlink(SMB_SHARE_DNAME);
86 
87 	if ((newfd = creat(SMB_SHARE_DNAME, 0644)) < 0) {
88 		syslog(LOG_ERR, "smb_share_dsrv_start: open: %s",
89 		    strerror(errno));
90 		(void) door_revoke(smb_share_dsrv_fd);
91 		smb_share_dsrv_fd = -1;
92 		(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
93 		return (-1);
94 	}
95 
96 	(void) close(newfd);
97 	(void) fdetach(SMB_SHARE_DNAME);
98 
99 	if (fattach(smb_share_dsrv_fd, SMB_SHARE_DNAME) < 0) {
100 		syslog(LOG_ERR, "smb_share_dsrv_start: fattach: %s",
101 		    strerror(errno));
102 		(void) door_revoke(smb_share_dsrv_fd);
103 		smb_share_dsrv_fd = -1;
104 		(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
105 		return (-1);
106 	}
107 
108 	(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
109 	return (smb_share_dsrv_fd);
110 }
111 
112 /*
113  * smb_share_dsrv_stop
114  *
115  * Stop the LanMan share door service.
116  */
117 void
118 smb_share_dsrv_stop(void)
119 {
120 	(void) pthread_mutex_lock(&smb_share_dsrv_mtx);
121 
122 	if (smb_share_dsrv_fd != -1) {
123 		(void) fdetach(SMB_SHARE_DNAME);
124 		(void) door_revoke(smb_share_dsrv_fd);
125 		smb_share_dsrv_fd = -1;
126 	}
127 
128 	(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
129 }
130 
131 /*
132  * smb_share_dsrv_dispatch
133  *
134  * This function with which the LMSHARE door is associated
135  * will invoke the appropriate CIFS share management function
136  * based on the request type of the door call.
137  */
138 /*ARGSUSED*/
139 static void
140 smb_share_dsrv_dispatch(void *cookie, char *ptr, size_t size, door_desc_t *dp,
141     uint_t n_desc)
142 {
143 	uint32_t rc;
144 	int req_type;
145 	char buf[SMB_SHARE_DSIZE];
146 	unsigned int used;
147 	smb_dr_ctx_t *dec_ctx;
148 	smb_dr_ctx_t *enc_ctx;
149 	unsigned int dec_status;
150 	unsigned int enc_status;
151 	char *sharename, *sharename2;
152 	char *cmnt, *ad_container;
153 	smb_share_t lmshr_info;
154 	smb_shrlist_t lmshr_list;
155 	smb_enumshare_info_t esi;
156 	int offset;
157 
158 	if ((cookie != SMB_SHARE_DSRV_COOKIE) || (ptr == NULL) ||
159 	    (size < sizeof (uint32_t))) {
160 		(void) door_return(NULL, 0, NULL, 0);
161 	}
162 
163 	dec_ctx = smb_dr_decode_start(ptr, size);
164 	enc_ctx = smb_dr_encode_start(buf, sizeof (buf));
165 	req_type = smb_dr_get_uint32(dec_ctx);
166 
167 	switch (req_type) {
168 	case SMB_SHROP_NUM_SHARES:
169 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0)
170 			goto decode_error;
171 
172 		rc = smb_shr_count();
173 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS);
174 		smb_dr_put_uint32(enc_ctx, rc);
175 		break;
176 
177 	case SMB_SHROP_DELETE:
178 		sharename = smb_dr_get_string(dec_ctx);
179 
180 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
181 			smb_dr_free_string(sharename);
182 			goto decode_error;
183 		}
184 
185 		rc = smb_shr_delete(sharename, B_FALSE);
186 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS);
187 		smb_dr_put_uint32(enc_ctx, rc);
188 		smb_dr_free_string(sharename);
189 		break;
190 
191 	case SMB_SHROP_RENAME:
192 		sharename = smb_dr_get_string(dec_ctx);
193 		sharename2 = smb_dr_get_string(dec_ctx);
194 
195 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
196 			smb_dr_free_string(sharename);
197 			smb_dr_free_string(sharename2);
198 			goto decode_error;
199 		}
200 
201 		rc = smb_shr_rename(sharename, sharename2);
202 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS);
203 		smb_dr_put_uint32(enc_ctx, rc);
204 		smb_dr_free_string(sharename);
205 		smb_dr_free_string(sharename2);
206 		break;
207 
208 	case SMB_SHROP_GETINFO:
209 		sharename = smb_dr_get_string(dec_ctx);
210 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
211 			smb_dr_free_string(sharename);
212 			goto decode_error;
213 		}
214 
215 		rc = smb_shr_get(sharename, &lmshr_info);
216 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS);
217 		smb_dr_put_uint32(enc_ctx, rc);
218 		smb_dr_put_share(enc_ctx, &lmshr_info);
219 		smb_dr_free_string(sharename);
220 		break;
221 
222 	case SMB_SHROP_ADD:
223 		smb_dr_get_share(dec_ctx, &lmshr_info);
224 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0)
225 			goto decode_error;
226 
227 		rc = smb_shr_create(&lmshr_info, B_FALSE);
228 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS);
229 		smb_dr_put_uint32(enc_ctx, rc);
230 		smb_dr_put_share(enc_ctx, &lmshr_info);
231 		break;
232 
233 	case SMB_SHROP_MODIFY:
234 		sharename = smb_dr_get_string(dec_ctx);
235 		cmnt = smb_dr_get_string(dec_ctx);
236 		ad_container = smb_dr_get_string(dec_ctx);
237 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
238 			smb_dr_free_string(sharename);
239 			smb_dr_free_string(cmnt);
240 			smb_dr_free_string(ad_container);
241 			goto decode_error;
242 		}
243 
244 		rc = smb_shr_modify(sharename, cmnt, ad_container, B_FALSE);
245 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS);
246 		smb_dr_put_uint32(enc_ctx, rc);
247 
248 		smb_dr_free_string(sharename);
249 		smb_dr_free_string(cmnt);
250 		smb_dr_free_string(ad_container);
251 		break;
252 
253 	case SMB_SHROP_LIST:
254 		offset = smb_dr_get_int32(dec_ctx);
255 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0)
256 			goto decode_error;
257 
258 		smb_shr_list(offset, &lmshr_list);
259 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS);
260 		smb_dr_put_buf(enc_ctx, (unsigned char *)&lmshr_list,
261 		    sizeof (smb_shrlist_t));
262 		break;
263 
264 	case SMB_SHROP_ENUM:
265 		esi.es_bufsize = smb_dr_get_ushort(dec_ctx);
266 		esi.es_username = smb_dr_get_string(dec_ctx);
267 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
268 			smb_dr_free_string(esi.es_username);
269 			goto decode_error;
270 		}
271 
272 		rc = smb_share_dsrv_enum(&esi);
273 
274 		smb_dr_free_string(esi.es_username);
275 
276 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DSUCCESS);
277 		smb_dr_put_uint32(enc_ctx, rc);
278 		if (rc == NERR_Success) {
279 			smb_dr_put_ushort(enc_ctx, esi.es_ntotal);
280 			smb_dr_put_ushort(enc_ctx, esi.es_nsent);
281 			smb_dr_put_ushort(enc_ctx, esi.es_datasize);
282 			smb_dr_put_buf(enc_ctx,
283 			    (unsigned char *)esi.es_buf, esi.es_bufsize);
284 			free(esi.es_buf);
285 		}
286 		break;
287 
288 	default:
289 		dec_status = smb_dr_decode_finish(dec_ctx);
290 		goto decode_error;
291 	}
292 
293 	if ((enc_status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
294 		enc_ctx = smb_dr_encode_start(buf, sizeof (buf));
295 		smb_dr_put_int32(enc_ctx, SMB_SHARE_DERROR);
296 		smb_dr_put_uint32(enc_ctx, enc_status);
297 		(void) smb_dr_encode_finish(enc_ctx, &used);
298 	}
299 
300 	(void) door_return(buf, used, NULL, 0);
301 	return;
302 
303 decode_error:
304 	smb_dr_put_int32(enc_ctx, SMB_SHARE_DERROR);
305 	smb_dr_put_uint32(enc_ctx, dec_status);
306 	(void) smb_dr_encode_finish(enc_ctx, &used);
307 	(void) door_return(buf, used, NULL, 0);
308 }
309 
310 /*
311  * smb_share_dsrv_enum
312  *
313  * This function builds a response for a NetShareEnum RAP request which
314  * originates from smbsrv kernel module. A response buffer is allocated
315  * with the specified size in esi->es_bufsize. List of shares is scanned
316  * twice. In the first round the total number of shares which their OEM
317  * name is shorter than 13 chars (esi->es_ntotal) and also the number of
318  * shares that fit in the given buffer are calculated. In the second
319  * round the shares data are encoded in the buffer.
320  *
321  * The data associated with each share has two parts, a fixed size part and
322  * a variable size part which is share's comment. The outline of the response
323  * buffer is so that fixed part for all the shares will appear first and follows
324  * with the comments for all those shares and that's why the data cannot be
325  * encoded in one round without unnecessarily complicating the code.
326  */
327 static int
328 smb_share_dsrv_enum(smb_enumshare_info_t *esi)
329 {
330 	smb_shriter_t shi;
331 	smb_share_t *si;
332 	int remained;
333 	uint16_t infolen = 0;
334 	uint16_t cmntlen = 0;
335 	uint16_t sharelen;
336 	uint16_t clen;
337 	uint32_t cmnt_offs;
338 	smb_msgbuf_t info_mb;
339 	smb_msgbuf_t cmnt_mb;
340 	boolean_t autohome_added = B_FALSE;
341 
342 	esi->es_ntotal = esi->es_nsent = 0;
343 
344 	if ((esi->es_buf = malloc(esi->es_bufsize)) == NULL)
345 		return (NERR_InternalError);
346 
347 	bzero(esi->es_buf, esi->es_bufsize);
348 	remained = esi->es_bufsize;
349 
350 	/* Do the necessary calculations in the first round */
351 	smb_shr_iterinit(&shi);
352 
353 	while ((si = smb_shr_iterate(&shi)) != NULL) {
354 		if (si->shr_flags & SMB_SHRF_LONGNAME)
355 			continue;
356 
357 		if ((si->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
358 			if (strcasecmp(esi->es_username, si->shr_name) == 0)
359 				autohome_added = B_TRUE;
360 			else
361 				continue;
362 		}
363 
364 		esi->es_ntotal++;
365 
366 		if (remained <= 0)
367 			continue;
368 
369 		clen = strlen(si->shr_cmnt) + 1;
370 		sharelen = SHARE_INFO_1_SIZE + clen;
371 
372 		if (sharelen <= remained) {
373 			infolen += SHARE_INFO_1_SIZE;
374 			cmntlen += clen;
375 		}
376 
377 		remained -= sharelen;
378 	}
379 
380 	esi->es_datasize = infolen + cmntlen;
381 
382 	smb_msgbuf_init(&info_mb, (uint8_t *)esi->es_buf, infolen, 0);
383 	smb_msgbuf_init(&cmnt_mb, (uint8_t *)esi->es_buf + infolen, cmntlen, 0);
384 	cmnt_offs = infolen;
385 
386 	/* Encode the data in the second round */
387 	smb_shr_iterinit(&shi);
388 	autohome_added = B_FALSE;
389 
390 	while ((si = smb_shr_iterate(&shi)) != NULL) {
391 		if (si->shr_flags & SMB_SHRF_LONGNAME)
392 			continue;
393 
394 		if ((si->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
395 			if (strcasecmp(esi->es_username, si->shr_name) == 0)
396 				autohome_added = B_TRUE;
397 			else
398 				continue;
399 		}
400 
401 		if (smb_msgbuf_encode(&info_mb, "13c.wl",
402 		    si->shr_oemname, si->shr_type, cmnt_offs) < 0)
403 			break;
404 
405 		if (smb_msgbuf_encode(&cmnt_mb, "s", si->shr_cmnt) < 0)
406 			break;
407 
408 		cmnt_offs += strlen(si->shr_cmnt) + 1;
409 		esi->es_nsent++;
410 	}
411 
412 	smb_msgbuf_term(&info_mb);
413 	smb_msgbuf_term(&cmnt_mb);
414 
415 	return (NERR_Success);
416 }
417