xref: /illumos-gate/usr/src/lib/libsmbfs/smb/connect.c (revision 9498083eeaed1aacdde41369b7fa6f3b84870791)
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