xref: /onnv-gate/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_rpcpipe.c (revision 5772:237ac22142fe)
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  * Functions to open and close named pipes. These functions are
30  * described in the CIFS 1.0 Protocol Specification (December 19, 1997).
31  */
32 
33 #include <alloca.h>
34 #include <pthread.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <syslog.h>
38 #include <synch.h>
39 
40 #include <smbsrv/libsmbrdr.h>
41 #include <smbsrv/ntstatus.h>
42 #include <smbrdr.h>
43 
44 static int smbrdr_close(struct sdb_ofile *);
45 static DWORD smbrdr_ntcreatex(struct sdb_ofile *);
46 static struct sdb_ofile *smbrdr_ofile_alloc(struct sdb_netuse *, char *);
47 
48 static void
49 smbrdr_ofile_clear(struct sdb_ofile *ofile)
50 {
51 	bzero(ofile, sizeof (struct sdb_ofile) - sizeof (mutex_t));
52 }
53 
54 static void
55 smbrdr_ofile_free(struct sdb_ofile *ofile)
56 {
57 	smbrdr_ofile_clear(ofile);
58 	(void) mutex_unlock(&ofile->mtx);
59 }
60 
61 
62 /*
63  * The ofile table.
64  */
65 static struct sdb_ofile ofile_table[N_OFILE_TABLE];
66 
67 static int mlsvc_pipe_recon_wait = 50;
68 static int mlsvc_pipe_recon_tries = 3;
69 
70 
71 /*
72  * mlsvc_open_pipe
73  *
74  * Open an RPC pipe on hostname. On success, return the fid. Otherwise
75  * returns a -ve error code.
76  */
77 int
78 mlsvc_open_pipe(char *hostname, char *domain, char *username, char *pipename)
79 {
80 	struct sdb_netuse *netuse;
81 	struct sdb_ofile *ofile;
82 	unsigned short tid;
83 	DWORD status;
84 	int retry;
85 	struct timespec st;
86 
87 	tid = smbrdr_tree_connect(hostname, username, "IPC$");
88 	if (tid == 0) {
89 		syslog(LOG_DEBUG, "smbrdr: (open) %s %s %s %s %s",
90 		    hostname, domain, username, pipename,
91 		    xlate_nt_status(NT_STATUS_UNEXPECTED_NETWORK_ERROR));
92 		return (-1);
93 	}
94 
95 	netuse = smbrdr_netuse_get(tid);
96 	if (netuse == NULL) {
97 		syslog(LOG_DEBUG, "smbrdr: (open) %s %s %s %s %s",
98 		    hostname, domain, username, pipename,
99 		    xlate_nt_status(NT_STATUS_CONNECTION_INVALID));
100 		return (-1);
101 	}
102 
103 	if ((ofile = smbrdr_ofile_alloc(netuse, pipename)) == 0) {
104 		syslog(LOG_DEBUG, "smbrdr: (open) %s %s %s %s %s",
105 		    hostname, domain, username, pipename,
106 		    xlate_nt_status(NT_STATUS_INSUFFICIENT_RESOURCES));
107 		smbrdr_netuse_put(netuse);
108 		return (-1);
109 	}
110 
111 	status = NT_STATUS_OPEN_FAILED;
112 
113 	for (retry = 0; retry < mlsvc_pipe_recon_tries; retry++) {
114 		status = smbrdr_ntcreatex(ofile);
115 
116 		switch (status) {
117 		case NT_STATUS_SUCCESS:
118 			(void) mutex_unlock(&ofile->mtx);
119 			smbrdr_netuse_put(netuse);
120 			return (ofile->fid);
121 
122 		case NT_STATUS_PIPE_NOT_AVAILABLE:
123 		case NT_STATUS_PIPE_BUSY:
124 			/*
125 			 * The server might return this error if it is
126 			 * temporarily busy or unable to create a pipe.
127 			 * We wait here before trying again to see if
128 			 * the pipe becomes available.
129 			 */
130 			st.tv_sec = 0;
131 			st.tv_nsec = mlsvc_pipe_recon_wait * 1000000;
132 			(void) nanosleep(&st, 0);
133 			break;
134 
135 		default:
136 			/*
137 			 * Something else went wrong: no more retries.
138 			 */
139 			retry = mlsvc_pipe_recon_tries;
140 			break;
141 		}
142 	}
143 
144 	syslog(LOG_DEBUG, "smbrdr: (open) %s %s %s %s %s",
145 	    hostname, domain, username, pipename,
146 	    xlate_nt_status(status));
147 	smbrdr_ofile_free(ofile);
148 	smbrdr_netuse_put(netuse);
149 	return (-1);
150 }
151 
152 /*
153  * mlsvc_close_pipe
154  *
155  * Close the named pipe represented by fid.
156  */
157 int
158 mlsvc_close_pipe(int fid)
159 {
160 	struct sdb_ofile *ofile;
161 	unsigned short tid;
162 	int rc;
163 
164 	if ((ofile = smbrdr_ofile_get(fid)) == NULL)
165 		return (-1);
166 
167 	tid = ofile->tid;
168 	rc = smbrdr_close(ofile);
169 	smbrdr_ofile_put(ofile);
170 
171 	if (rc == 0)
172 		(void) smbrdr_tree_disconnect(tid);
173 
174 	return (rc);
175 }
176 
177 /*
178  * smbrdr_ofile_put
179  *
180  * Unlock given ofile structure.
181  */
182 void
183 smbrdr_ofile_put(struct sdb_ofile *ofile)
184 {
185 	if (ofile)
186 		(void) mutex_unlock(&ofile->mtx);
187 }
188 
189 /*
190  * smbrdr_ofile_get
191  *
192  * Locate the ofile for the specified fid. Just to be safe, ensure that
193  * the netuse pointer is valid. Return a pointer to the ofile structure.
194  * Return a null pointer if a valid ofile cannot be found.
195  */
196 struct sdb_ofile *
197 smbrdr_ofile_get(int fid)
198 {
199 	struct sdb_session *session;
200 	struct sdb_netuse *netuse;
201 	struct sdb_ofile *ofile;
202 	int i;
203 
204 	for (i = 0; i < N_OFILE_TABLE; ++i) {
205 		ofile = &ofile_table[i];
206 
207 		(void) mutex_lock(&ofile->mtx);
208 
209 		if (ofile->fid == fid) {
210 			session = ofile->session;
211 			netuse = ofile->netuse;
212 
213 			/*
214 			 * status check:
215 			 * make sure all the structures are in the right state
216 			 */
217 			if (session && netuse &&
218 			    (ofile->state == SDB_FSTATE_OPEN) &&
219 			    (netuse->state == SDB_NSTATE_CONNECTED) &&
220 			    (session->logon.state == SDB_LSTATE_SETUP) &&
221 			    (session->state == SDB_SSTATE_NEGOTIATED)) {
222 				/* sanity check */
223 				if ((ofile->sid == session->sid) &&
224 				    (ofile->uid == session->logon.uid) &&
225 				    (ofile->tid == netuse->tid)) {
226 					return (ofile);
227 				} else {
228 					/* invalid structure */
229 					smbrdr_ofile_clear(ofile);
230 				}
231 			}
232 		}
233 
234 		(void) mutex_unlock(&ofile->mtx);
235 	}
236 
237 	return (NULL);
238 }
239 
240 /*
241  * smbrdr_ofile_end_of_share
242  *
243  * This function can be used when closing a share to ensure that all
244  * ofiles resources are released. Don't call mlsvc_close_pipe because
245  * that will call mlsvc_smb_tdcon and we don't know what state
246  * the share is in. The server will probably close all files anyway.
247  * We are more interested in releasing the ofile resources.
248  */
249 void
250 smbrdr_ofile_end_of_share(unsigned short tid)
251 {
252 	struct sdb_ofile *ofile;
253 	int i;
254 
255 	for (i = 0; i < N_OFILE_TABLE; ++i) {
256 		ofile = &ofile_table[i];
257 		(void) mutex_lock(&ofile->mtx);
258 		if (ofile->tid == tid)
259 			(void) smbrdr_close(ofile);
260 		(void) mutex_unlock(&ofile->mtx);
261 	}
262 }
263 
264 /*
265  * smbrdr_dump_ofiles
266  *
267  * Dump the open files table.
268  */
269 void
270 smbrdr_dump_ofiles()
271 {
272 	struct sdb_ofile *ofile;
273 	struct sdb_netuse *netuse;
274 	int i;
275 
276 	for (i = 0; i < N_OFILE_TABLE; ++i) {
277 		ofile = &ofile_table[i];
278 		(void) mutex_lock(&ofile->mtx);
279 		netuse = ofile->netuse;
280 
281 		if (netuse) {
282 			syslog(LOG_DEBUG, "file[%d]: %s (fid=%d)", i,
283 			    ofile->path, ofile->fid);
284 			syslog(LOG_DEBUG,
285 			    "file[%d]: session(%d), user(%d), tree(%d)",
286 			    i, netuse->session->sock, netuse->uid,
287 			    netuse->tid);
288 		}
289 		(void) mutex_unlock(&ofile->mtx);
290 	}
291 }
292 
293 /*
294  * Private Functions
295  */
296 
297 /*
298  * smbrdr_close
299  *
300  * Send SMBClose request for the given open file.
301  */
302 static int
303 smbrdr_close(struct sdb_ofile *ofile)
304 {
305 	struct sdb_session *session;
306 	struct sdb_netuse *netuse;
307 	struct sdb_logon *logon;
308 	smbrdr_handle_t srh;
309 	smb_hdr_t smb_hdr;
310 	DWORD status;
311 	int fid;
312 	int rc;
313 
314 	if (ofile == NULL)
315 		return (0);
316 
317 	ofile->state = SDB_FSTATE_CLOSING;
318 
319 	if ((session = ofile->session) == NULL) {
320 		smbrdr_ofile_clear(ofile);
321 		return (0);
322 	}
323 
324 	if ((session->state != SDB_SSTATE_NEGOTIATED) &&
325 	    (session->state != SDB_SSTATE_DISCONNECTING)) {
326 		smbrdr_ofile_clear(ofile);
327 		return (0);
328 	}
329 
330 	fid = ofile->fid;
331 
332 	netuse = ofile->netuse;
333 	logon = &session->logon;
334 
335 	status = smbrdr_request_init(&srh, SMB_COM_CLOSE,
336 	    session, logon, netuse);
337 
338 	if (status != NT_STATUS_SUCCESS) {
339 		smbrdr_ofile_clear(ofile);
340 		return (-1);
341 	}
342 
343 	rc = smb_msgbuf_encode(&srh.srh_mbuf, "bwlw.", 3, fid, 0x00000000ul, 0);
344 	if (rc <= 0) {
345 		smbrdr_handle_free(&srh);
346 		smbrdr_ofile_clear(ofile);
347 		return (-1);
348 	}
349 
350 	status = smbrdr_exchange(&srh, &smb_hdr, 0);
351 	if (status != NT_STATUS_SUCCESS)
352 		syslog(LOG_DEBUG, "smbrdr_close: %s", xlate_nt_status(status));
353 
354 	smbrdr_handle_free(&srh);
355 	smbrdr_ofile_clear(ofile);
356 	return (0);
357 }
358 
359 /*
360  * smbrdr_ofile_alloc
361  *
362  * Allocate an ofile for the specified name. File info is associated
363  * with a share so we need a valid share before calling this function.
364  * If a slot is already allocated to the specified file, a pointer to
365  * that slot is returned. Otherwise we allocate and initialize a new
366  * slot in the table. If the table is full, a null pointer will be
367  * returned.
368  */
369 static struct sdb_ofile *
370 smbrdr_ofile_alloc(struct sdb_netuse *netuse, char *name)
371 {
372 	struct sdb_ofile *ofile;
373 	int i;
374 
375 	for (i = 0; i < N_OFILE_TABLE; ++i) {
376 		ofile = &ofile_table[i];
377 
378 		(void) mutex_lock(&ofile->mtx);
379 		if (ofile->netuse == 0) {
380 
381 			ofile->session = netuse->session;
382 			ofile->netuse = netuse;
383 			ofile->sid = netuse->session->sid;
384 			ofile->uid = netuse->session->logon.uid;
385 			ofile->tid = netuse->tid;
386 			ofile->fid = 0;
387 			(void) strcpy(ofile->path, name);
388 			ofile->state = SDB_FSTATE_INIT;
389 			return (ofile);
390 		}
391 
392 		(void) mutex_unlock(&ofile->mtx);
393 	}
394 
395 	return (NULL);
396 }
397 
398 /*
399  * smbrdr_ntcreatex
400  *
401  * This will do an SMB_COM_NT_CREATE_ANDX with lots of default values.
402  * All of the underlying session and share data should already be set
403  * up before we get here. If everything works we'll get a valid fid.
404  */
405 static DWORD
406 smbrdr_ntcreatex(struct sdb_ofile *ofile)
407 {
408 	struct sdb_logon *logon;
409 	struct sdb_netuse *netuse;
410 	struct sdb_session *sess;
411 	smbrdr_handle_t srh;
412 	smb_hdr_t smb_hdr;
413 	smb_msgbuf_t *mb;
414 	char *path;
415 	unsigned path_len;
416 	int data_bytes;
417 	int rc;
418 	unsigned short fid;
419 	int null_size;
420 	DWORD status;
421 
422 	netuse = ofile->netuse;
423 	sess = netuse->session;
424 	logon = &sess->logon;
425 
426 	/*
427 	 * If this was a general purpose interface, we should support
428 	 * full UNC semantics but we only use this for RPC over named
429 	 * pipes with well-known endpoints.
430 	 */
431 	path_len = strlen(ofile->path) + 2;
432 	path = alloca(path_len);
433 
434 	if (ofile->path[0] != '\\')
435 		(void) snprintf(path, path_len, "\\%s", ofile->path);
436 	else
437 		(void) strcpy(path, ofile->path);
438 
439 	if (sess->remote_caps & CAP_UNICODE) {
440 		path_len = mts_wcequiv_strlen(path);
441 		null_size = sizeof (mts_wchar_t);
442 	} else {
443 		path_len = strlen(path);
444 		null_size = sizeof (char);
445 	}
446 
447 	syslog(LOG_DEBUG, "smbrdr_ntcreatex: %d %s", path_len, path);
448 
449 	status = smbrdr_request_init(&srh, SMB_COM_NT_CREATE_ANDX,
450 	    sess, logon, netuse);
451 
452 	if (status != NT_STATUS_SUCCESS) {
453 		syslog(LOG_DEBUG, "smbrdr_ntcreatex: %s",
454 		    xlate_nt_status(status));
455 		return (NT_STATUS_INVALID_PARAMETER_1);
456 	}
457 
458 	mb = &srh.srh_mbuf;
459 
460 	data_bytes = path_len + null_size;
461 
462 	rc = smb_msgbuf_encode(mb,
463 	    "(wct)b (andx)b1.w (resv). (nlen)w (flg)l"
464 	    "(rdf)l (dacc)l (allo)q (efa)l (shr)l (cdisp)l (copt)l (impl)l"
465 	    "(secf)b (bcc)w (name)u",
466 	    24,				/* smb_wct */
467 	    0xff,			/* AndXCommand (none) */
468 	    0x0000,			/* AndXOffset */
469 	    path_len,			/* Unicode NameLength */
470 	    0x00000006ul,		/* Flags (oplocks) */
471 	    0,				/* RootDirectoryFid */
472 	    0x0002019Ful,		/* DesiredAccess */
473 	    0x0ull,			/* AllocationSize */
474 	    0x00000000ul,		/* ExtFileAttributes */
475 	    0x00000003ul,		/* ShareAccess (RW) */
476 	    0x00000001ul,		/* CreateDisposition (OpenExisting) */
477 	    0x00000000ul,		/* CreateOptions */
478 	    0x00000002ul,		/* ImpersonationLevel */
479 	    0x01u,			/* SecurityFlags */
480 	    data_bytes,			/* smb_bcc */
481 	    path);			/* Name */
482 
483 	if (rc <= 0) {
484 		smbrdr_handle_free(&srh);
485 		return (NT_STATUS_INVALID_PARAMETER_1);
486 	}
487 
488 	status = smbrdr_exchange(&srh, &smb_hdr, 0);
489 	if (status != NT_STATUS_SUCCESS) {
490 		smbrdr_handle_free(&srh);
491 		return (NT_SC_VALUE(status));
492 	}
493 
494 	rc = smb_msgbuf_decode(mb, "(wct). (andx)4. (opl)1. (fid)w", &fid);
495 	if (rc <= 0) {
496 		smbrdr_handle_free(&srh);
497 		return (NT_STATUS_INVALID_PARAMETER_2);
498 	}
499 
500 	ofile->fid = fid;
501 	ofile->state = SDB_FSTATE_OPEN;
502 	syslog(LOG_DEBUG, "SmbRdrNtCreate: fid=%d", ofile->fid);
503 	smbrdr_handle_free(&srh);
504 	return (NT_STATUS_SUCCESS);
505 }
506