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	"%Z%%M%	%I%	%E% 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/lmshare.h>
46 #include <smbsrv/lmshare_door.h>
47 #include <smbsrv/smbinfo.h>
48 
49 static int smb_share_dsrv_fd = -1;
50 static pthread_mutex_t smb_share_dsrv_mtx = PTHREAD_MUTEX_INITIALIZER;
51 
52 static void smb_share_dsrv_dispatch(void *, char *, size_t, door_desc_t *,
53     uint_t);
54 static int smb_share_dsrv_check(int, char *);
55 static int smb_share_dsrv_enum(smb_enumshare_info_t *esi);
56 
57 /*
58  * smb_share_dsrv_start
59  *
60  * Start the LanMan share door service.
61  * Returns 0 on success. Otherwise, -1.
62  */
63 int
64 smb_share_dsrv_start(void)
65 {
66 	int	newfd;
67 
68 	(void) pthread_mutex_lock(&smb_share_dsrv_mtx);
69 
70 	if (smb_share_dsrv_fd != -1) {
71 		syslog(LOG_ERR, "smb_share_dsrv_start: duplicate");
72 		(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
73 		return (smb_share_dsrv_fd);
74 	}
75 
76 	if ((smb_share_dsrv_fd = door_create(smb_share_dsrv_dispatch,
77 	    LMSHR_DOOR_COOKIE, (DOOR_UNREF | DOOR_REFUSE_DESC))) < 0) {
78 		syslog(LOG_ERR, "smb_share_dsrv_start: door_create: %s",
79 		    strerror(errno));
80 		(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
81 		return (-1);
82 	}
83 
84 	(void) unlink(LMSHR_DOOR_NAME);
85 
86 	if ((newfd = creat(LMSHR_DOOR_NAME, 0644)) < 0) {
87 		syslog(LOG_ERR, "smb_share_dsrv_start: open: %s",
88 		    strerror(errno));
89 		(void) door_revoke(smb_share_dsrv_fd);
90 		smb_share_dsrv_fd = -1;
91 		(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
92 		return (-1);
93 	}
94 
95 	(void) close(newfd);
96 	(void) fdetach(LMSHR_DOOR_NAME);
97 
98 	if (fattach(smb_share_dsrv_fd, LMSHR_DOOR_NAME) < 0) {
99 		syslog(LOG_ERR, "smb_share_dsrv_start: fattach: %s",
100 		    strerror(errno));
101 		(void) door_revoke(smb_share_dsrv_fd);
102 		smb_share_dsrv_fd = -1;
103 		(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
104 		return (-1);
105 	}
106 
107 	(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
108 	return (smb_share_dsrv_fd);
109 }
110 
111 /*
112  * smb_share_dsrv_stop
113  *
114  * Stop the LanMan share door service.
115  */
116 void
117 smb_share_dsrv_stop(void)
118 {
119 	(void) pthread_mutex_lock(&smb_share_dsrv_mtx);
120 
121 	if (smb_share_dsrv_fd != -1) {
122 		(void) fdetach(LMSHR_DOOR_NAME);
123 		(void) door_revoke(smb_share_dsrv_fd);
124 		smb_share_dsrv_fd = -1;
125 	}
126 
127 	(void) pthread_mutex_unlock(&smb_share_dsrv_mtx);
128 }
129 
130 /*
131  * smb_share_dsrv_dispatch
132  *
133  * This function with which the LMSHARE door is associated
134  * will invoke the appropriate CIFS share management function
135  * based on the request type of the door call.
136  */
137 /*ARGSUSED*/
138 static void
139 smb_share_dsrv_dispatch(void *cookie, char *ptr, size_t size, door_desc_t *dp,
140     uint_t n_desc)
141 {
142 	DWORD rc;
143 	int req_type, rc2;
144 	char buf[LMSHR_DOOR_SIZE];
145 	unsigned int used;
146 	smb_dr_ctx_t *dec_ctx;
147 	smb_dr_ctx_t *enc_ctx;
148 	unsigned int dec_status;
149 	unsigned int enc_status;
150 	char *sharename, *sharename2;
151 	lmshare_info_t lmshr_info;
152 	lmshare_list_t lmshr_list;
153 	smb_enumshare_info_t esi;
154 	int offset;
155 
156 	if ((cookie != LMSHR_DOOR_COOKIE) || (ptr == NULL) ||
157 	    (size < sizeof (uint32_t))) {
158 		(void) door_return(NULL, 0, NULL, 0);
159 	}
160 
161 	dec_ctx = smb_dr_decode_start(ptr, size);
162 	enc_ctx = smb_dr_encode_start(buf, sizeof (buf));
163 	req_type = smb_dr_get_uint32(dec_ctx);
164 
165 	switch (req_type) {
166 	case LMSHR_DOOR_NUM_SHARES:
167 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0)
168 			goto decode_error;
169 
170 		rc = lmshare_num_shares();
171 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
172 		smb_dr_put_uint32(enc_ctx, rc);
173 		break;
174 
175 	case LMSHR_DOOR_DELETE:
176 		sharename = smb_dr_get_string(dec_ctx);
177 
178 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
179 			smb_dr_free_string(sharename);
180 			goto decode_error;
181 		}
182 
183 		rc = lmshare_delete(sharename, 0);
184 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
185 		smb_dr_put_uint32(enc_ctx, rc);
186 		smb_dr_free_string(sharename);
187 		break;
188 
189 	case LMSHR_DOOR_RENAME:
190 		sharename = smb_dr_get_string(dec_ctx);
191 		sharename2 = smb_dr_get_string(dec_ctx);
192 
193 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
194 			smb_dr_free_string(sharename);
195 			smb_dr_free_string(sharename2);
196 			goto decode_error;
197 		}
198 
199 		rc = lmshare_rename(sharename, sharename2, 0);
200 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
201 		smb_dr_put_uint32(enc_ctx, rc);
202 		smb_dr_free_string(sharename);
203 		smb_dr_free_string(sharename2);
204 		break;
205 
206 	case LMSHR_DOOR_GETINFO:
207 		sharename = smb_dr_get_string(dec_ctx);
208 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
209 			smb_dr_free_string(sharename);
210 			goto decode_error;
211 		}
212 
213 		rc = lmshare_getinfo(sharename, &lmshr_info);
214 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
215 		smb_dr_put_uint32(enc_ctx, rc);
216 		smb_dr_put_lmshare(enc_ctx, &lmshr_info);
217 		smb_dr_free_string(sharename);
218 		break;
219 
220 	case LMSHR_DOOR_ADD:
221 		smb_dr_get_lmshare(dec_ctx, &lmshr_info);
222 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0)
223 			goto decode_error;
224 
225 		rc = lmshare_add(&lmshr_info, 0);
226 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
227 		smb_dr_put_uint32(enc_ctx, rc);
228 		smb_dr_put_lmshare(enc_ctx, &lmshr_info);
229 		break;
230 
231 	case LMSHR_DOOR_SETINFO:
232 		smb_dr_get_lmshare(dec_ctx, &lmshr_info);
233 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0)
234 			goto decode_error;
235 
236 		rc = lmshare_setinfo(&lmshr_info, 0);
237 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
238 		smb_dr_put_uint32(enc_ctx, rc);
239 		break;
240 
241 	case LMSHR_DOOR_EXISTS:
242 	case LMSHR_DOOR_IS_SPECIAL:
243 	case LMSHR_DOOR_IS_RESTRICTED:
244 	case LMSHR_DOOR_IS_ADMIN:
245 	case LMSHR_DOOR_IS_VALID:
246 	case LMSHR_DOOR_IS_DIR:
247 		sharename = smb_dr_get_string(dec_ctx);
248 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
249 			smb_dr_free_string(sharename);
250 			goto decode_error;
251 		}
252 
253 		rc2 = smb_share_dsrv_check(req_type, sharename);
254 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
255 		smb_dr_put_uint32(enc_ctx, rc2);
256 		smb_dr_free_string(sharename);
257 		break;
258 
259 	case LMSHR_DOOR_LIST:
260 		offset = smb_dr_get_int32(dec_ctx);
261 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0)
262 			goto decode_error;
263 
264 		lmshare_list(offset, &lmshr_list);
265 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
266 		smb_dr_put_lmshr_list(enc_ctx, &lmshr_list);
267 		break;
268 
269 	case LMSHR_DOOR_ENUM:
270 		esi.es_bufsize = smb_dr_get_ushort(dec_ctx);
271 		esi.es_username = smb_dr_get_string(dec_ctx);
272 		if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) {
273 			smb_dr_free_string(esi.es_username);
274 			goto decode_error;
275 		}
276 
277 		rc = smb_share_dsrv_enum(&esi);
278 
279 		smb_dr_free_string(esi.es_username);
280 
281 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS);
282 		smb_dr_put_uint32(enc_ctx, rc);
283 		if (rc == NERR_Success) {
284 			smb_dr_put_ushort(enc_ctx, esi.es_ntotal);
285 			smb_dr_put_ushort(enc_ctx, esi.es_nsent);
286 			smb_dr_put_ushort(enc_ctx, esi.es_datasize);
287 			smb_dr_put_buf(enc_ctx,
288 			    (unsigned char *)esi.es_buf, esi.es_bufsize);
289 			free(esi.es_buf);
290 		}
291 		break;
292 
293 	default:
294 		dec_status = smb_dr_decode_finish(dec_ctx);
295 		goto decode_error;
296 	}
297 
298 	if ((enc_status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
299 		enc_ctx = smb_dr_encode_start(buf, sizeof (buf));
300 		smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_ERROR);
301 		smb_dr_put_uint32(enc_ctx, enc_status);
302 		(void) smb_dr_encode_finish(enc_ctx, &used);
303 	}
304 
305 	(void) door_return(buf, used, NULL, 0);
306 	return;
307 
308 decode_error:
309 	smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_ERROR);
310 	smb_dr_put_uint32(enc_ctx, dec_status);
311 	(void) smb_dr_encode_finish(enc_ctx, &used);
312 	(void) door_return(buf, used, NULL, 0);
313 }
314 
315 /*
316  * smb_share_dsrv_check
317  *
318  * Depending upon the opcode, this function will
319  * either check the existence of a share/dir or
320  * the the type of the specified share.
321  */
322 static int
323 smb_share_dsrv_check(int opcode, char *sharename)
324 {
325 	int rc;
326 
327 	switch (opcode) {
328 	case LMSHR_DOOR_EXISTS:
329 		rc = lmshare_exists(sharename);
330 		break;
331 
332 	case LMSHR_DOOR_IS_SPECIAL:
333 		rc = lmshare_is_special(sharename);
334 		break;
335 
336 	case LMSHR_DOOR_IS_RESTRICTED:
337 		rc = lmshare_is_restricted(sharename);
338 		break;
339 
340 	case LMSHR_DOOR_IS_ADMIN:
341 		rc = lmshare_is_admin(sharename);
342 		break;
343 
344 	case LMSHR_DOOR_IS_VALID:
345 		rc = lmshare_is_valid(sharename);
346 		break;
347 
348 	case LMSHR_DOOR_IS_DIR:
349 		rc = lmshare_is_dir(sharename);
350 	}
351 
352 	return (rc);
353 }
354 
355 /*
356  * smb_share_dsrv_enum
357  *
358  * This function builds a response for a NetShareEnum RAP request which
359  * originates from smbsrv kernel module. A response buffer is allocated
360  * with the specified size in esi->es_bufsize. List of shares is scanned
361  * twice. In the first round the total number of shares which their OEM
362  * name is shorter than 13 chars (esi->es_ntotal) and also the number of
363  * shares that fit in the given buffer are calculated. In the second
364  * round the shares data are encoded in the buffer.
365  *
366  * The data associated with each share has two parts, a fixed size part and
367  * a variable size part which is share's comment. The outline of the response
368  * buffer is so that fixed part for all the shares will appear first and follows
369  * with the comments for all those shares and that's why the data cannot be
370  * encoded in one round without unnecessarily complicating the code.
371  */
372 static int
373 smb_share_dsrv_enum(smb_enumshare_info_t *esi)
374 {
375 	lmshare_iterator_t shi;
376 	lmshare_info_t *si;
377 	int remained;
378 	uint16_t infolen = 0;
379 	uint16_t cmntlen = 0;
380 	uint16_t sharelen;
381 	uint16_t clen;
382 	uint32_t cmnt_offs;
383 	smb_msgbuf_t info_mb;
384 	smb_msgbuf_t cmnt_mb;
385 	boolean_t autohome_added = B_FALSE;
386 
387 	esi->es_ntotal = esi->es_nsent = 0;
388 
389 	if ((esi->es_buf = malloc(esi->es_bufsize)) == NULL)
390 		return (NERR_InternalError);
391 
392 	bzero(esi->es_buf, esi->es_bufsize);
393 	remained = esi->es_bufsize;
394 
395 	/* Do the necessary calculations in the first round */
396 	lmshare_init_iterator(&shi, LMSHRM_ALL);
397 
398 	while ((si = lmshare_iterate(&shi)) != NULL) {
399 		if (si->mode & LMSHRM_LONGNAME)
400 			continue;
401 
402 		if ((si->mode & LMSHRM_AUTOHOME) && !autohome_added) {
403 			if (strcasecmp(esi->es_username, si->share_name) == 0)
404 				autohome_added = B_TRUE;
405 			else
406 				continue;
407 		}
408 
409 		esi->es_ntotal++;
410 
411 		if (remained <= 0)
412 			continue;
413 
414 		clen = strlen(si->comment) + 1;
415 		sharelen = SHARE_INFO_1_SIZE + clen;
416 
417 		if (sharelen <= remained) {
418 			infolen += SHARE_INFO_1_SIZE;
419 			cmntlen += clen;
420 		}
421 
422 		remained -= sharelen;
423 	}
424 
425 	esi->es_datasize = infolen + cmntlen;
426 
427 	smb_msgbuf_init(&info_mb, (uint8_t *)esi->es_buf, infolen, 0);
428 	smb_msgbuf_init(&cmnt_mb, (uint8_t *)esi->es_buf + infolen, cmntlen, 0);
429 	cmnt_offs = infolen;
430 
431 	/* Encode the data in the second round */
432 	lmshare_init_iterator(&shi, LMSHRM_ALL);
433 	autohome_added = B_FALSE;
434 
435 	while ((si = lmshare_iterate(&shi)) != NULL) {
436 		if (si->mode & LMSHRM_LONGNAME)
437 			continue;
438 
439 		if ((si->mode & LMSHRM_AUTOHOME) && !autohome_added) {
440 			if (strcasecmp(esi->es_username, si->share_name) == 0)
441 				autohome_added = B_TRUE;
442 			else
443 				continue;
444 		}
445 
446 		if (smb_msgbuf_encode(&info_mb, "13c.wl",
447 		    si->oem_name, si->stype, cmnt_offs) < 0)
448 			break;
449 
450 		if (smb_msgbuf_encode(&cmnt_mb, "s", si->comment) < 0)
451 			break;
452 
453 		cmnt_offs += strlen(si->comment) + 1;
454 		esi->es_nsent++;
455 	}
456 
457 	smb_msgbuf_term(&info_mb);
458 	smb_msgbuf_term(&cmnt_mb);
459 
460 	return (NERR_Success);
461 }
462