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