xref: /titanic_51/usr/src/lib/libsmbfs/smb/connect.c (revision 85e6b6747d07050e01ec91acef2453655821f9ab)
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  /*
23   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24   * Use is subject to license terms.
25   * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
26   */
27  
28  /*
29   * Functions to setup connections (TCP and/or NetBIOS)
30   * This has the fall-back logic for IP6, IP4, NBT
31   */
32  
33  #include <errno.h>
34  #include <stdio.h>
35  #include <string.h>
36  #include <strings.h>
37  #include <stdlib.h>
38  #include <unistd.h>
39  #include <netdb.h>
40  #include <libintl.h>
41  #include <xti.h>
42  #include <assert.h>
43  
44  #include <sys/types.h>
45  #include <sys/time.h>
46  #include <sys/byteorder.h>
47  #include <sys/socket.h>
48  #include <sys/fcntl.h>
49  
50  #include <netinet/in.h>
51  #include <netinet/tcp.h>
52  #include <arpa/inet.h>
53  
54  #include <netsmb/smb.h>
55  #include <netsmb/smb_lib.h>
56  #include <netsmb/netbios.h>
57  #include <netsmb/nb_lib.h>
58  #include <netsmb/smb_dev.h>
59  
60  #include "charsets.h"
61  #include "private.h"
62  
63  /*
64   * SMB messages are up to 64K.
65   * Let's leave room for two.
66   */
67  static int smb_tcpsndbuf = 0x20000;
68  static int smb_tcprcvbuf = 0x20000;
69  static int smb_connect_timeout = 30; /* seconds */
70  int smb_recv_timeout = 30; /* seconds */
71  
72  int conn_tcp6(struct smb_ctx *, const struct sockaddr *, int);
73  int conn_tcp4(struct smb_ctx *, const struct sockaddr *, int);
74  int conn_nbt(struct smb_ctx *, const struct sockaddr *, char *);
75  
76  /*
77   * Internal set sockopt for int-sized options.
78   * Borrowed from: libnsl/rpc/ti_opts.c
79   */
80  static int
81  smb_setopt_int(int fd, int level, int name, int val)
82  {
83  	struct t_optmgmt oreq, ores;
84  	struct {
85  		struct t_opthdr oh;
86  		int ival;
87  	} opts;
88  
89  	/* opt header */
90  	opts.oh.len = sizeof (opts);
91  	opts.oh.level = level;
92  	opts.oh.name = name;
93  	opts.oh.status = 0;
94  	opts.ival = val;
95  
96  	oreq.flags = T_NEGOTIATE;
97  	oreq.opt.buf = (void *)&opts;
98  	oreq.opt.len = sizeof (opts);
99  
100  	ores.flags = 0;
101  	ores.opt.buf = NULL;
102  	ores.opt.maxlen = 0;
103  
104  	if (t_optmgmt(fd, &oreq, &ores) < 0) {
105  		DPRINT("t_opgmgnt, t_errno = %d", t_errno);
106  		if (t_errno == TSYSERR)
107  			return (errno);
108  		return (EPROTO);
109  	}
110  	if (ores.flags != T_SUCCESS) {
111  		DPRINT("flags 0x%x, status 0x%x",
112  		    (int)ores.flags, (int)opts.oh.status);
113  		return (EPROTO);
114  	}
115  
116  	return (0);
117  }
118  
119  static int
120  smb_setopts(int fd)
121  {
122  	int err;
123  
124  	/*
125  	 * Set various socket/TCP options.
126  	 * Failures here are not fatal -
127  	 * just log a complaint.
128  	 *
129  	 * We don't need these two:
130  	 *   SO_RCVTIMEO, SO_SNDTIMEO
131  	 */
132  
133  	err = smb_setopt_int(fd, SOL_SOCKET, SO_SNDBUF, smb_tcpsndbuf);
134  	if (err) {
135  		DPRINT("set SO_SNDBUF, err %d", err);
136  	}
137  
138  	err = smb_setopt_int(fd, SOL_SOCKET, SO_RCVBUF, smb_tcprcvbuf);
139  	if (err) {
140  		DPRINT("set SO_RCVBUF, err %d", err);
141  	}
142  
143  	err = smb_setopt_int(fd, SOL_SOCKET, SO_KEEPALIVE, 1);
144  	if (err) {
145  		DPRINT("set SO_KEEPALIVE, err %d", err);
146  	}
147  
148  	err = smb_setopt_int(fd, IPPROTO_TCP, TCP_NODELAY, 1);
149  	if (err) {
150  		DPRINT("set TCP_NODELAY, err %d", err);
151  	}
152  
153  	/* Set the connect timeout (in milliseconds). */
154  	err = smb_setopt_int(fd, IPPROTO_TCP,
155  	    TCP_CONN_ABORT_THRESHOLD,
156  	    smb_connect_timeout * 1000);
157  	if (err) {
158  		DPRINT("set connect timeout, err %d", err);
159  	}
160  	return (0);
161  }
162  
163  
164  int
165  conn_tcp6(struct smb_ctx *ctx, const struct sockaddr *sa, int port)
166  {
167  	struct sockaddr_in6 sin6;
168  	char *dev = "/dev/tcp6";
169  	char paddrbuf[INET6_ADDRSTRLEN];
170  	struct t_call sndcall;
171  	int fd, err;
172  
173  	if (sa->sa_family != AF_INET6) {
174  		DPRINT("bad af %d", sa->sa_family);
175  		return (EINVAL);
176  	}
177  	bcopy(sa, &sin6, sizeof (sin6));
178  	sin6.sin6_port = htons(port);
179  
180  	DPRINT("tcp6: %s (%d)",
181  	    inet_ntop(AF_INET6, &sin6.sin6_addr,
182  	    paddrbuf, sizeof (paddrbuf)), port);
183  
184  	fd = t_open(dev, O_RDWR, NULL);
185  	if (fd < 0) {
186  		/* Assume t_errno = TSYSERR */
187  		err = errno;
188  		perror(dev);
189  		return (err);
190  	}
191  	if ((err = smb_setopts(fd)) != 0)
192  		goto errout;
193  	if (t_bind(fd, NULL, NULL) < 0) {
194  		DPRINT("t_bind t_errno %d", t_errno);
195  		if (t_errno == TSYSERR)
196  			err = errno;
197  		else
198  			err = EPROTO;
199  		goto errout;
200  	}
201  	sndcall.addr.maxlen = sizeof (sin6);
202  	sndcall.addr.len = sizeof (sin6);
203  	sndcall.addr.buf = (void *) &sin6;
204  	sndcall.opt.len = 0;
205  	sndcall.udata.len = 0;
206  	if (t_connect(fd, &sndcall, NULL) < 0) {
207  		err = get_xti_err(fd);
208  		DPRINT("connect, err %d", err);
209  		goto errout;
210  	}
211  
212  	DPRINT("tcp6: connected, fd=%d", fd);
213  	ctx->ct_tran_fd = fd;
214  	return (0);
215  
216  errout:
217  	close(fd);
218  	return (err);
219  }
220  
221  /*
222   * This is used for both SMB over TCP (port 445)
223   * and NetBIOS - see conn_nbt().
224   */
225  int
226  conn_tcp4(struct smb_ctx *ctx, const struct sockaddr *sa, int port)
227  {
228  	struct sockaddr_in sin;
229  	char *dev = "/dev/tcp";
230  	char paddrbuf[INET_ADDRSTRLEN];
231  	struct t_call sndcall;
232  	int fd, err;
233  
234  	if (sa->sa_family != AF_INET) {
235  		DPRINT("bad af %d", sa->sa_family);
236  		return (EINVAL);
237  	}
238  	bcopy(sa, &sin, sizeof (sin));
239  	sin.sin_port = htons(port);
240  
241  	DPRINT("tcp4: %s (%d)",
242  	    inet_ntop(AF_INET, &sin.sin_addr,
243  	    paddrbuf, sizeof (paddrbuf)), port);
244  
245  	fd = t_open(dev, O_RDWR, NULL);
246  	if (fd < 0) {
247  		/* Assume t_errno = TSYSERR */
248  		err = errno;
249  		perror(dev);
250  		return (err);
251  	}
252  	if ((err = smb_setopts(fd)) != 0)
253  		goto errout;
254  	if (t_bind(fd, NULL, NULL) < 0) {
255  		DPRINT("t_bind t_errno %d", t_errno);
256  		if (t_errno == TSYSERR)
257  			err = errno;
258  		else
259  			err = EPROTO;
260  		goto errout;
261  	}
262  	sndcall.addr.maxlen = sizeof (sin);
263  	sndcall.addr.len = sizeof (sin);
264  	sndcall.addr.buf = (void *) &sin;
265  	sndcall.opt.len = 0;
266  	sndcall.udata.len = 0;
267  	if (t_connect(fd, &sndcall, NULL) < 0) {
268  		err = get_xti_err(fd);
269  		DPRINT("connect, err %d", err);
270  		goto errout;
271  	}
272  
273  	DPRINT("tcp4: connected, fd=%d", fd);
274  	ctx->ct_tran_fd = fd;
275  	return (0);
276  
277  errout:
278  	close(fd);
279  	return (err);
280  }
281  
282  /*
283   * Open a NetBIOS connection (session, port 139)
284   *
285   * The optional name parameter, if passed, means
286   * we found the sockaddr via NetBIOS name lookup,
287   * and can just use that for our session request.
288   * Otherwise (if name is NULL), we're connecting
289   * by IP address, and need to come up with the
290   * NetBIOS name by other means.
291   */
292  int
293  conn_nbt(struct smb_ctx *ctx, const struct sockaddr *saarg, char *name)
294  {
295  	struct sockaddr_in sin;
296  	struct sockaddr *sa;
297  	char server[NB_NAMELEN];
298  	char workgroup[NB_NAMELEN];
299  	int err, nberr, port;
300  
301  	bcopy(saarg, &sin, sizeof (sin));
302  	sa = (struct sockaddr *)&sin;
303  
304  	switch (sin.sin_family) {
305  	case AF_NETBIOS:	/* our fake AF */
306  		sin.sin_family = AF_INET;
307  		break;
308  	case AF_INET:
309  		break;
310  	default:
311  		DPRINT("bad af %d", sin.sin_family);
312  		return (EINVAL);
313  	}
314  	port = IPPORT_NETBIOS_SSN;
315  
316  	/*
317  	 * If we have a NetBIOS name, just use it.
318  	 * This is the path taken when we've done a
319  	 * NetBIOS name lookup on this name to get
320  	 * the IP address in the passed sa. Otherwise,
321  	 * we're connecting by IP address, and need to
322  	 * figure out what NetBIOS name to use.
323  	 */
324  	if (name) {
325  		strlcpy(server, name, sizeof (server));
326  		DPRINT("given name: %s", server);
327  	} else {
328  		/*
329  		 *
330  		 * Try a NetBIOS node status query,
331  		 * which searches for a type=[20] name.
332  		 * If that doesn't work, just use the
333  		 * (fake) "*SMBSERVER" name.
334  		 */
335  		DPRINT("try node status");
336  		server[0] = '\0';
337  		nberr = nbns_getnodestatus(ctx->ct_nb,
338  		    &sin.sin_addr, server, workgroup);
339  		if (nberr == 0 && server[0] != '\0') {
340  			/* Found the name.  Save for reconnect. */
341  			DPRINT("found name: %s", server);
342  			strlcpy(ctx->ct_srvname, server,
343  			    sizeof (ctx->ct_srvname));
344  		} else {
345  			DPRINT("getnodestatus, nberr %d", nberr);
346  			strlcpy(server, "*SMBSERVER", sizeof (server));
347  		}
348  	}
349  
350  	/*
351  	 * Establish the TCP connection.
352  	 * Careful to close it on errors.
353  	 */
354  	if ((err = conn_tcp4(ctx, sa, port)) != 0) {
355  		DPRINT("TCP connect: err=%d", err);
356  		goto out;
357  	}
358  
359  	/* Connected.  Do NetBIOS session request. */
360  	err = nb_ssn_request(ctx, server);
361  	if (err)
362  		DPRINT("ssn_rq, err %d", err);
363  
364  out:
365  	if (err) {
366  		if (ctx->ct_tran_fd != -1) {
367  			close(ctx->ct_tran_fd);
368  			ctx->ct_tran_fd = -1;
369  		}
370  	}
371  	return (err);
372  }
373  
374  /*
375   * Make a new connection, or reconnect.
376   */
377  int
378  smb_iod_connect(smb_ctx_t *ctx)
379  {
380  	struct sockaddr *sa;
381  	int err, err2;
382  	struct mbdata blob;
383  
384  	memset(&blob, 0, sizeof (blob));
385  
386  	if (ctx->ct_srvname[0] == '\0') {
387  		DPRINT("sername not set!");
388  		return (EINVAL);
389  	}
390  	DPRINT("server: %s", ctx->ct_srvname);
391  
392  	if (smb_debug)
393  		dump_ctx("smb_iod_connect", ctx);
394  
395  	/*
396  	 * This may be a reconnect, so
397  	 * cleanup if necessary.
398  	 */
399  	if (ctx->ct_tran_fd != -1) {
400  		close(ctx->ct_tran_fd);
401  		ctx->ct_tran_fd = -1;
402  	}
403  
404  	/*
405  	 * Get local machine name.
406  	 * Full name - not a NetBIOS name.
407  	 */
408  	if (ctx->ct_locname == NULL) {
409  		err = smb_getlocalname(&ctx->ct_locname);
410  		if (err) {
411  			smb_error(dgettext(TEXT_DOMAIN,
412  			    "can't get local name"), err);
413  			return (err);
414  		}
415  	}
416  
417  	/*
418  	 * We're called with each IP address
419  	 * already copied into ct_srvaddr.
420  	 */
421  	ctx->ct_flags |= SMBCF_RESOLVED;
422  
423  	sa = &ctx->ct_srvaddr.sa;
424  	switch (sa->sa_family) {
425  
426  	case AF_INET6:
427  		err = conn_tcp6(ctx, sa, IPPORT_SMB);
428  		break;
429  
430  	case AF_INET:
431  		err = conn_tcp4(ctx, sa, IPPORT_SMB);
432  		/*
433  		 * If port 445 was not listening, try port 139.
434  		 * Note: Not doing NetBIOS name lookup here.
435  		 * We already have the IP address.
436  		 */
437  		switch (err) {
438  		case ECONNRESET:
439  		case ECONNREFUSED:
440  			err2 = conn_nbt(ctx, sa, NULL);
441  			if (err2 == 0)
442  				err = 0;
443  		}
444  		break;
445  
446  	case AF_NETBIOS:
447  		/* Like AF_INET, but use NetBIOS ssn. */
448  		err = conn_nbt(ctx, sa, ctx->ct_srvname);
449  		break;
450  
451  	default:
452  		DPRINT("skipped family %d", sa->sa_family);
453  		err = EPROTONOSUPPORT;
454  		break;
455  	}
456  
457  
458  	if (err) {
459  		DPRINT("connect, err=%d", err);
460  		return (err);
461  	}
462  
463  	/*
464  	 * Do SMB Negotiate Protocol.
465  	 */
466  	err = smb_negprot(ctx, &blob);
467  	if (err)
468  		goto out;
469  
470  	/*
471  	 * Empty user name means an explicit request for
472  	 * NULL session setup, which is a special case.
473  	 * If negotiate determined that we want to do
474  	 * SMB signing, we have to turn that off for a
475  	 * NULL session. [MS-SMB 3.3.5.3].
476  	 */
477  	if (ctx->ct_user[0] == '\0') {
478  		/* Null user should have null domain too. */
479  		ctx->ct_domain[0] = '\0';
480  		ctx->ct_authflags = SMB_AT_ANON;
481  		ctx->ct_clnt_caps &= ~SMB_CAP_EXT_SECURITY;
482  		ctx->ct_vcflags &= ~SMBV_WILL_SIGN;
483  	}
484  
485  	/*
486  	 * Do SMB Session Setup (authenticate)
487  	 *
488  	 * If the server negotiated extended security,
489  	 * run the SPNEGO state machine, otherwise do
490  	 * one of the old-style variants.
491  	 */
492  	if (ctx->ct_clnt_caps & SMB_CAP_EXT_SECURITY) {
493  		err = smb_ssnsetup_spnego(ctx, &blob);
494  	} else {
495  		/*
496  		 * Server did NOT negotiate extended security.
497  		 * Try NTLMv2, NTLMv1, or ANON (if enabled).
498  		 */
499  		if (ctx->ct_authflags & SMB_AT_NTLM2) {
500  			err = smb_ssnsetup_ntlm2(ctx);
501  		} else if (ctx->ct_authflags & SMB_AT_NTLM1) {
502  			err = smb_ssnsetup_ntlm1(ctx);
503  		} else if (ctx->ct_authflags & SMB_AT_ANON) {
504  			err = smb_ssnsetup_null(ctx);
505  		} else {
506  			/*
507  			 * Don't return EAUTH, because a new
508  			 * password prompt will not help.
509  			 */
510  			DPRINT("No NTLM authflags");
511  			err = ENOTSUP;
512  		}
513  	}
514  
515  out:
516  	mb_done(&blob);
517  
518  	if (err) {
519  		close(ctx->ct_tran_fd);
520  		ctx->ct_tran_fd = -1;
521  	} else {
522  		/* Tell library code we have a session. */
523  		ctx->ct_flags |= SMBCF_SSNACTIVE;
524  		DPRINT("tran_fd = %d", ctx->ct_tran_fd);
525  	}
526  
527  	return (err);
528  }
529