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