xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c (revision bfed486ad8de8b8ebc6345a8e10accae08bf2f45)
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 /*
27  * Net DFS server side RPC service.
28  */
29 
30 #include <sys/types.h>
31 #include <strings.h>
32 #include <string.h>
33 
34 #include <smbsrv/libsmb.h>
35 #include <smbsrv/lmerr.h>
36 #include <smbsrv/lmdfs.h>
37 #include <smbsrv/nmpipes.h>
38 #include <smbsrv/nterror.h>
39 #include <smbsrv/libmlrpc.h>
40 #include <smbsrv/ndl/netdfs.ndl>
41 
42 typedef struct {
43 	char *server;
44 	char *share;
45 	char *path;
46 	char *buf;
47 } netdfs_unc_t;
48 
49 static int netdfs_unc_parse(ndr_xa_t *, const char *,
50     netdfs_unc_t *);
51 
52 static int netdfs_s_getver(void *, ndr_xa_t *);
53 static int netdfs_s_add(void *, ndr_xa_t *);
54 static int netdfs_s_remove(void *, ndr_xa_t *);
55 static int netdfs_s_setinfo(void *, ndr_xa_t *);
56 static int netdfs_s_getinfo(void *, ndr_xa_t *);
57 static int netdfs_s_enum(void *, ndr_xa_t *);
58 static int netdfs_s_move(void *, ndr_xa_t *);
59 static int netdfs_s_rename(void *, ndr_xa_t *);
60 static int netdfs_s_addstdroot(void *, ndr_xa_t *);
61 static int netdfs_s_remstdroot(void *, ndr_xa_t *);
62 static int netdfs_s_enumex(void *, ndr_xa_t *);
63 
64 static ndr_stub_table_t netdfs_stub_table[] = {
65 	{ netdfs_s_getver,	NETDFS_OPNUM_GETVER },
66 	{ netdfs_s_add,		NETDFS_OPNUM_ADD },
67 	{ netdfs_s_remove,	NETDFS_OPNUM_REMOVE },
68 	{ netdfs_s_setinfo,	NETDFS_OPNUM_SETINFO },
69 	{ netdfs_s_getinfo,	NETDFS_OPNUM_GETINFO },
70 	{ netdfs_s_enum,	NETDFS_OPNUM_ENUM },
71 	{ netdfs_s_rename,	NETDFS_OPNUM_RENAME },
72 	{ netdfs_s_move,	NETDFS_OPNUM_MOVE },
73 	{ netdfs_s_addstdroot,	NETDFS_OPNUM_ADDSTDROOT },
74 	{ netdfs_s_remstdroot,	NETDFS_OPNUM_REMSTDROOT },
75 	{ netdfs_s_enumex,	NETDFS_OPNUM_ENUMEX },
76 	{0}
77 };
78 
79 static ndr_service_t netdfs_service = {
80 	"NETDFS",			/* name */
81 	"DFS",				/* desc */
82 	"\\dfs",			/* endpoint */
83 	PIPE_NTSVCS,			/* sec_addr_port */
84 	NETDFS_ABSTRACT_UUID,	NETDFS_ABSTRACT_VERS,
85 	NETDFS_TRANSFER_UUID,	NETDFS_TRANSFER_VERS,
86 
87 	0,				/* no bind_instance_size */
88 	0,				/* no bind_req() */
89 	0,				/* no unbind_and_close() */
90 	0,				/* use generic_call_stub() */
91 
92 	&TYPEINFO(netdfs_interface),	/* interface ti */
93 	netdfs_stub_table		/* stub_table */
94 };
95 
96 /*
97  * Register the NETDFS RPC interface with the RPC runtime library.
98  * The service must be registered in order to use either the client
99  * side or the server side functions.
100  */
101 void
102 netdfs_initialize(void)
103 {
104 	(void) ndr_svc_register(&netdfs_service);
105 }
106 
107 /*
108  * Return the version.
109  *
110  * We have to indicate that we emulate a Windows 2003 Server or the
111  * client will not use the EnumEx RPC and this would limit support
112  * to a single DFS root.
113  */
114 /*ARGSUSED*/
115 static int
116 netdfs_s_getver(void *arg, ndr_xa_t *mxa)
117 {
118 	struct netdfs_getver *param = arg;
119 
120 	param->version = DFS_MANAGER_VERSION_W2K3;
121 	return (NDR_DRC_OK);
122 }
123 
124 /*
125  * Add a new volume or additional storage for an existing volume at
126  * dfs_path.
127  */
128 static int
129 netdfs_s_add(void *arg, ndr_xa_t *mxa)
130 {
131 	struct netdfs_add *param = arg;
132 	netdfs_unc_t unc;
133 	DWORD status = ERROR_SUCCESS;
134 
135 	if (param->dfs_path == NULL || param->server == NULL ||
136 	    param->share == NULL) {
137 		bzero(param, sizeof (struct netdfs_add));
138 		param->status = ERROR_INVALID_PARAMETER;
139 		return (NDR_DRC_OK);
140 	}
141 
142 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
143 		status = ERROR_INVALID_PARAMETER;
144 	} else {
145 		if (unc.path == NULL)
146 			status = ERROR_BAD_PATHNAME;
147 
148 		if (unc.share == NULL)
149 			status = ERROR_INVALID_SHARENAME;
150 	}
151 
152 	if (param->status != ERROR_SUCCESS) {
153 		bzero(param, sizeof (struct netdfs_add));
154 		param->status = status;
155 		return (NDR_DRC_OK);
156 	}
157 
158 	bzero(param, sizeof (struct netdfs_add));
159 	param->status = ERROR_ACCESS_DENIED;
160 	return (NDR_DRC_OK);
161 }
162 
163 /*
164  * netdfs_s_remove
165  *
166  * Remove a volume or additional storage for volume from the DFS at
167  * dfs_path. When applied to the last storage in a volume, removes
168  * the volume from the DFS.
169  */
170 static int
171 netdfs_s_remove(void *arg, ndr_xa_t *mxa)
172 {
173 	struct netdfs_remove *param = arg;
174 	netdfs_unc_t unc;
175 	DWORD status = ERROR_SUCCESS;
176 
177 	if (param->dfs_path == NULL || param->server == NULL ||
178 	    param->share == NULL) {
179 		bzero(param, sizeof (struct netdfs_remove));
180 		param->status = ERROR_INVALID_PARAMETER;
181 		return (NDR_DRC_OK);
182 	}
183 
184 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
185 		status = ERROR_INVALID_PARAMETER;
186 	} else {
187 		if (unc.path == NULL)
188 			status = ERROR_BAD_PATHNAME;
189 
190 		if (unc.share == NULL)
191 			status = ERROR_INVALID_SHARENAME;
192 	}
193 
194 	if (param->status != ERROR_SUCCESS) {
195 		bzero(param, sizeof (struct netdfs_remove));
196 		param->status = status;
197 		return (NDR_DRC_OK);
198 	}
199 
200 	bzero(param, sizeof (struct netdfs_remove));
201 	param->status = ERROR_ACCESS_DENIED;
202 	return (NDR_DRC_OK);
203 }
204 
205 /*
206  * Set information about the volume or storage. If the server and share
207  * are specified, the information set is specific to that server and
208  * share. Otherwise the information is specific to the volume as a whole.
209  *
210  * Valid levels are 100-102.
211  */
212 /*ARGSUSED*/
213 static int
214 netdfs_s_setinfo(void *arg, ndr_xa_t *mxa)
215 {
216 	struct netdfs_setinfo *param = arg;
217 	netdfs_unc_t unc;
218 	DWORD status = ERROR_SUCCESS;
219 
220 	if (param->dfs_path == NULL) {
221 		bzero(param, sizeof (struct netdfs_setinfo));
222 		param->status = ERROR_INVALID_PARAMETER;
223 		return (NDR_DRC_OK);
224 	}
225 
226 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
227 		status = ERROR_INVALID_PARAMETER;
228 	} else {
229 		if (unc.share == NULL)
230 			status = ERROR_INVALID_SHARENAME;
231 	}
232 
233 	if (param->status != ERROR_SUCCESS) {
234 		bzero(param, sizeof (struct netdfs_setinfo));
235 		param->status = status;
236 		return (NDR_DRC_OK);
237 	}
238 
239 	switch (param->info.level) {
240 	case 100:
241 	case 101:
242 	case 102:
243 		break;
244 
245 	default:
246 		bzero(param, sizeof (struct netdfs_setinfo));
247 		param->status = ERROR_INVALID_LEVEL;
248 		return (NDR_DRC_OK);
249 	}
250 
251 	bzero(param, sizeof (struct netdfs_setinfo));
252 	param->status = ERROR_ACCESS_DENIED;
253 	return (NDR_DRC_OK);
254 }
255 
256 /*
257  * Get information about the volume or storage. If the server and share
258  * are specified, the information returned is specific to that server
259  * and share. Otherwise the information is specific to the volume as a
260  * whole.
261  *
262  * Valid levels are 1-4, 100-104.
263  */
264 /*ARGSUSED*/
265 static int
266 netdfs_s_getinfo(void *arg, ndr_xa_t *mxa)
267 {
268 	struct netdfs_getinfo *param = arg;
269 	netdfs_unc_t unc;
270 	DWORD status = ERROR_SUCCESS;
271 
272 	if (param->dfs_path == NULL) {
273 		bzero(param, sizeof (struct netdfs_getinfo));
274 		param->status = ERROR_INVALID_PARAMETER;
275 		return (NDR_DRC_OK);
276 	}
277 
278 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
279 		status = ERROR_INVALID_PARAMETER;
280 	} else {
281 		if (unc.share == NULL)
282 			status = ERROR_INVALID_SHARENAME;
283 	}
284 
285 	if (param->status != ERROR_SUCCESS) {
286 		bzero(param, sizeof (struct netdfs_getinfo));
287 		param->status = status;
288 		return (NDR_DRC_OK);
289 	}
290 
291 	switch (param->level) {
292 	case 1:
293 	case 2:
294 	case 3:
295 	case 4:
296 	case 100:
297 	case 101:
298 	case 102:
299 	case 103:
300 	case 104:
301 		break;
302 
303 	default:
304 		bzero(param, sizeof (struct netdfs_getinfo));
305 		param->status = ERROR_INVALID_LEVEL;
306 		return (NDR_DRC_OK);
307 	}
308 
309 	bzero(param, sizeof (struct netdfs_getinfo));
310 	param->status = ERROR_ACCESS_DENIED;
311 	return (NDR_DRC_OK);
312 }
313 
314 /*
315  * Get information about all of the volumes in the DFS. dfs_name is
316  * the "server" part of the UNC name used to refer to this particular
317  * DFS.
318  *
319  * Valid levels are 1-3.
320  */
321 /*ARGSUSED*/
322 static int
323 netdfs_s_enum(void *arg, ndr_xa_t *mxa)
324 {
325 	struct netdfs_enum *param = arg;
326 
327 	switch (param->level) {
328 	case 1:
329 	case 2:
330 	case 3:
331 		break;
332 
333 	default:
334 		(void) bzero(param, sizeof (struct netdfs_enum));
335 		param->status = ERROR_INVALID_LEVEL;
336 		return (NDR_DRC_OK);
337 	}
338 
339 	(void) bzero(param, sizeof (struct netdfs_enum));
340 	param->status = ERROR_ACCESS_DENIED;
341 	return (NDR_DRC_OK);
342 }
343 
344 /*
345  * Move a DFS volume and all subordinate volumes from one place in the
346  * DFS to another place in the DFS.
347  */
348 /*ARGSUSED*/
349 static int
350 netdfs_s_move(void *arg, ndr_xa_t *mxa)
351 {
352 	struct netdfs_move *param = arg;
353 
354 	if (param->dfs_path == NULL || param->new_path == NULL) {
355 		bzero(param, sizeof (struct netdfs_move));
356 		param->status = ERROR_INVALID_PARAMETER;
357 		return (NDR_DRC_OK);
358 	}
359 
360 	bzero(param, sizeof (struct netdfs_move));
361 	param->status = ERROR_ACCESS_DENIED;
362 	return (NDR_DRC_OK);
363 }
364 
365 /*
366  * Rename the current path in a DFS to a new path in the same DFS.
367  */
368 /*ARGSUSED*/
369 static int
370 netdfs_s_rename(void *arg, ndr_xa_t *mxa)
371 {
372 	struct netdfs_rename *param = arg;
373 
374 	if (param->dfs_path == NULL || param->new_path == NULL) {
375 		bzero(param, sizeof (struct netdfs_rename));
376 		param->status = ERROR_INVALID_PARAMETER;
377 		return (NDR_DRC_OK);
378 	}
379 
380 	bzero(param, sizeof (struct netdfs_rename));
381 	param->status = ERROR_ACCESS_DENIED;
382 	return (NDR_DRC_OK);
383 }
384 
385 /*
386  * Add a DFS root share.
387  */
388 /*ARGSUSED*/
389 static int
390 netdfs_s_addstdroot(void *arg, ndr_xa_t *mxa)
391 {
392 	struct netdfs_addstdroot *param = arg;
393 
394 	bzero(param, sizeof (struct netdfs_addstdroot));
395 	param->status = ERROR_INVALID_PARAMETER;
396 	return (NDR_DRC_OK);
397 }
398 
399 /*
400  * Remove a DFS root share.
401  */
402 /*ARGSUSED*/
403 static int
404 netdfs_s_remstdroot(void *arg, ndr_xa_t *mxa)
405 {
406 	struct netdfs_remstdroot *param = arg;
407 
408 	bzero(param, sizeof (struct netdfs_remstdroot));
409 	param->status = ERROR_INVALID_PARAMETER;
410 	return (NDR_DRC_OK);
411 }
412 
413 /*
414  * Get information about all of the volumes in the DFS. dfs_path is
415  * the "server" part of the UNC name used to refer to this particular
416  * DFS.
417  *
418  * Valid levels are 1-3, 300.
419  */
420 static int
421 netdfs_s_enumex(void *arg, ndr_xa_t *mxa)
422 {
423 	struct netdfs_enumex *param = arg;
424 	netdfs_unc_t unc;
425 	DWORD status = ERROR_SUCCESS;
426 
427 	if (param->dfs_path == NULL) {
428 		bzero(param, sizeof (struct netdfs_enumex));
429 		param->status = ERROR_INVALID_PARAMETER;
430 		return (NDR_DRC_OK);
431 	}
432 
433 	if (param->resume_handle == NULL)
434 		param->resume_handle = NDR_NEW(mxa, DWORD);
435 
436 	if (param->resume_handle)
437 		*(param->resume_handle) = 0;
438 
439 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
440 		status = ERROR_INVALID_PARAMETER;
441 	} else {
442 		if (unc.path == NULL)
443 			status = ERROR_BAD_PATHNAME;
444 
445 		if (unc.share == NULL)
446 			status = ERROR_INVALID_SHARENAME;
447 	}
448 
449 	if (param->status != ERROR_SUCCESS) {
450 		bzero(param, sizeof (struct netdfs_enumex));
451 		param->status = status;
452 		return (NDR_DRC_OK);
453 	}
454 
455 	param->info = NDR_NEW(mxa, struct netdfs_enum_info);
456 	if (param->info == NULL) {
457 		bzero(param, sizeof (struct netdfs_enumex));
458 		param->status = ERROR_NOT_ENOUGH_MEMORY;
459 		return (NDR_DRC_OK);
460 	}
461 
462 	bzero(param->info, sizeof (struct netdfs_enumex));
463 	param->status = ERROR_SUCCESS;
464 	return (NDR_DRC_OK);
465 }
466 
467 /*
468  * Parse a UNC path (\\server\share\path) into components.
469  * Path separators are converted to forward slashes.
470  *
471  * Returns 0 on success, otherwise -1 to indicate an error.
472  */
473 static int
474 netdfs_unc_parse(ndr_xa_t *mxa, const char *path, netdfs_unc_t *unc)
475 {
476 	char *p;
477 
478 	if (path == NULL || unc == NULL)
479 		return (-1);
480 
481 	if ((unc->buf = NDR_STRDUP(mxa, (char *)path)) == NULL)
482 		return (-1);
483 
484 	if ((p = strchr(unc->buf, '\n')) != NULL)
485 		*p = '\0';
486 
487 	(void) strsubst(unc->buf, '\\', '/');
488 	(void) strcanon(unc->buf, "/");
489 
490 	unc->server = unc->buf;
491 	unc->server += strspn(unc->buf, "/");
492 
493 	if (unc->server) {
494 		unc->share = strchr(unc->server, '/');
495 		if ((p = unc->share) != NULL) {
496 			unc->share += strspn(unc->share, "/");
497 			*p = '\0';
498 		}
499 	}
500 
501 	if (unc->share) {
502 		unc->path = strchr(unc->share, '/');
503 		if ((p = unc->path) != NULL) {
504 			unc->path += strspn(unc->path, "/");
505 			*p = '\0';
506 		}
507 	}
508 
509 	if (unc->path) {
510 		if ((p = strchr(unc->path, '\0')) != NULL) {
511 			if (*(--p) == '/')
512 				*p = '\0';
513 		}
514 	}
515 
516 	return (0);
517 }
518