1613a2f6bSGordon Ross /*
2613a2f6bSGordon Ross * CDDL HEADER START
3613a2f6bSGordon Ross *
4613a2f6bSGordon Ross * The contents of this file are subject to the terms of the
5613a2f6bSGordon Ross * Common Development and Distribution License (the "License").
6613a2f6bSGordon Ross * You may not use this file except in compliance with the License.
7613a2f6bSGordon Ross *
8613a2f6bSGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9613a2f6bSGordon Ross * or http://www.opensolaris.org/os/licensing.
10613a2f6bSGordon Ross * See the License for the specific language governing permissions
11613a2f6bSGordon Ross * and limitations under the License.
12613a2f6bSGordon Ross *
13613a2f6bSGordon Ross * When distributing Covered Code, include this CDDL HEADER in each
14613a2f6bSGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15613a2f6bSGordon Ross * If applicable, add the following below this CDDL HEADER, with the
16613a2f6bSGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying
17613a2f6bSGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner]
18613a2f6bSGordon Ross *
19613a2f6bSGordon Ross * CDDL HEADER END
20613a2f6bSGordon Ross */
21613a2f6bSGordon Ross
22613a2f6bSGordon Ross /*
23613a2f6bSGordon Ross * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24613a2f6bSGordon Ross * Use is subject to license terms.
25*85e6b674SGordon Ross * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
26613a2f6bSGordon Ross */
27613a2f6bSGordon Ross
28613a2f6bSGordon Ross /*
29613a2f6bSGordon Ross * Functions to setup connections (TCP and/or NetBIOS)
30613a2f6bSGordon Ross * This has the fall-back logic for IP6, IP4, NBT
31613a2f6bSGordon Ross */
32613a2f6bSGordon Ross
33613a2f6bSGordon Ross #include <errno.h>
34613a2f6bSGordon Ross #include <stdio.h>
35613a2f6bSGordon Ross #include <string.h>
36613a2f6bSGordon Ross #include <strings.h>
37613a2f6bSGordon Ross #include <stdlib.h>
38613a2f6bSGordon Ross #include <unistd.h>
39613a2f6bSGordon Ross #include <netdb.h>
40613a2f6bSGordon Ross #include <libintl.h>
41613a2f6bSGordon Ross #include <xti.h>
42613a2f6bSGordon Ross #include <assert.h>
43613a2f6bSGordon Ross
44613a2f6bSGordon Ross #include <sys/types.h>
45613a2f6bSGordon Ross #include <sys/time.h>
46613a2f6bSGordon Ross #include <sys/byteorder.h>
47613a2f6bSGordon Ross #include <sys/socket.h>
48613a2f6bSGordon Ross #include <sys/fcntl.h>
49613a2f6bSGordon Ross
50613a2f6bSGordon Ross #include <netinet/in.h>
51613a2f6bSGordon Ross #include <netinet/tcp.h>
52613a2f6bSGordon Ross #include <arpa/inet.h>
53613a2f6bSGordon Ross
54613a2f6bSGordon Ross #include <netsmb/smb.h>
55613a2f6bSGordon Ross #include <netsmb/smb_lib.h>
56613a2f6bSGordon Ross #include <netsmb/netbios.h>
57613a2f6bSGordon Ross #include <netsmb/nb_lib.h>
58613a2f6bSGordon Ross #include <netsmb/smb_dev.h>
59613a2f6bSGordon Ross
60613a2f6bSGordon Ross #include "charsets.h"
61613a2f6bSGordon Ross #include "private.h"
62613a2f6bSGordon Ross
63613a2f6bSGordon Ross /*
64613a2f6bSGordon Ross * SMB messages are up to 64K.
65613a2f6bSGordon Ross * Let's leave room for two.
66613a2f6bSGordon Ross */
67613a2f6bSGordon Ross static int smb_tcpsndbuf = 0x20000;
68613a2f6bSGordon Ross static int smb_tcprcvbuf = 0x20000;
69613a2f6bSGordon Ross static int smb_connect_timeout = 30; /* seconds */
70613a2f6bSGordon Ross int smb_recv_timeout = 30; /* seconds */
71613a2f6bSGordon Ross
72613a2f6bSGordon Ross int conn_tcp6(struct smb_ctx *, const struct sockaddr *, int);
73613a2f6bSGordon Ross int conn_tcp4(struct smb_ctx *, const struct sockaddr *, int);
74613a2f6bSGordon Ross int conn_nbt(struct smb_ctx *, const struct sockaddr *, char *);
75613a2f6bSGordon Ross
76613a2f6bSGordon Ross /*
77613a2f6bSGordon Ross * Internal set sockopt for int-sized options.
78613a2f6bSGordon Ross * Borrowed from: libnsl/rpc/ti_opts.c
79613a2f6bSGordon Ross */
80613a2f6bSGordon Ross static int
smb_setopt_int(int fd,int level,int name,int val)81613a2f6bSGordon Ross smb_setopt_int(int fd, int level, int name, int val)
82613a2f6bSGordon Ross {
83613a2f6bSGordon Ross struct t_optmgmt oreq, ores;
84613a2f6bSGordon Ross struct {
85613a2f6bSGordon Ross struct t_opthdr oh;
86613a2f6bSGordon Ross int ival;
87613a2f6bSGordon Ross } opts;
88613a2f6bSGordon Ross
89613a2f6bSGordon Ross /* opt header */
90613a2f6bSGordon Ross opts.oh.len = sizeof (opts);
91613a2f6bSGordon Ross opts.oh.level = level;
92613a2f6bSGordon Ross opts.oh.name = name;
93613a2f6bSGordon Ross opts.oh.status = 0;
94613a2f6bSGordon Ross opts.ival = val;
95613a2f6bSGordon Ross
96613a2f6bSGordon Ross oreq.flags = T_NEGOTIATE;
97613a2f6bSGordon Ross oreq.opt.buf = (void *)&opts;
98613a2f6bSGordon Ross oreq.opt.len = sizeof (opts);
99613a2f6bSGordon Ross
100613a2f6bSGordon Ross ores.flags = 0;
101613a2f6bSGordon Ross ores.opt.buf = NULL;
102613a2f6bSGordon Ross ores.opt.maxlen = 0;
103613a2f6bSGordon Ross
104613a2f6bSGordon Ross if (t_optmgmt(fd, &oreq, &ores) < 0) {
105613a2f6bSGordon Ross DPRINT("t_opgmgnt, t_errno = %d", t_errno);
106613a2f6bSGordon Ross if (t_errno == TSYSERR)
107613a2f6bSGordon Ross return (errno);
108613a2f6bSGordon Ross return (EPROTO);
109613a2f6bSGordon Ross }
110613a2f6bSGordon Ross if (ores.flags != T_SUCCESS) {
111613a2f6bSGordon Ross DPRINT("flags 0x%x, status 0x%x",
112613a2f6bSGordon Ross (int)ores.flags, (int)opts.oh.status);
113613a2f6bSGordon Ross return (EPROTO);
114613a2f6bSGordon Ross }
115613a2f6bSGordon Ross
116613a2f6bSGordon Ross return (0);
117613a2f6bSGordon Ross }
118613a2f6bSGordon Ross
119613a2f6bSGordon Ross static int
smb_setopts(int fd)120613a2f6bSGordon Ross smb_setopts(int fd)
121613a2f6bSGordon Ross {
122613a2f6bSGordon Ross int err;
123613a2f6bSGordon Ross
124613a2f6bSGordon Ross /*
125613a2f6bSGordon Ross * Set various socket/TCP options.
126613a2f6bSGordon Ross * Failures here are not fatal -
127613a2f6bSGordon Ross * just log a complaint.
128613a2f6bSGordon Ross *
129613a2f6bSGordon Ross * We don't need these two:
130613a2f6bSGordon Ross * SO_RCVTIMEO, SO_SNDTIMEO
131613a2f6bSGordon Ross */
132613a2f6bSGordon Ross
133613a2f6bSGordon Ross err = smb_setopt_int(fd, SOL_SOCKET, SO_SNDBUF, smb_tcpsndbuf);
134613a2f6bSGordon Ross if (err) {
135613a2f6bSGordon Ross DPRINT("set SO_SNDBUF, err %d", err);
136613a2f6bSGordon Ross }
137613a2f6bSGordon Ross
138613a2f6bSGordon Ross err = smb_setopt_int(fd, SOL_SOCKET, SO_RCVBUF, smb_tcprcvbuf);
139613a2f6bSGordon Ross if (err) {
140613a2f6bSGordon Ross DPRINT("set SO_RCVBUF, err %d", err);
141613a2f6bSGordon Ross }
142613a2f6bSGordon Ross
143613a2f6bSGordon Ross err = smb_setopt_int(fd, SOL_SOCKET, SO_KEEPALIVE, 1);
144613a2f6bSGordon Ross if (err) {
145613a2f6bSGordon Ross DPRINT("set SO_KEEPALIVE, err %d", err);
146613a2f6bSGordon Ross }
147613a2f6bSGordon Ross
148613a2f6bSGordon Ross err = smb_setopt_int(fd, IPPROTO_TCP, TCP_NODELAY, 1);
149613a2f6bSGordon Ross if (err) {
150613a2f6bSGordon Ross DPRINT("set TCP_NODELAY, err %d", err);
151613a2f6bSGordon Ross }
152613a2f6bSGordon Ross
153613a2f6bSGordon Ross /* Set the connect timeout (in milliseconds). */
154613a2f6bSGordon Ross err = smb_setopt_int(fd, IPPROTO_TCP,
155613a2f6bSGordon Ross TCP_CONN_ABORT_THRESHOLD,
156613a2f6bSGordon Ross smb_connect_timeout * 1000);
157613a2f6bSGordon Ross if (err) {
158613a2f6bSGordon Ross DPRINT("set connect timeout, err %d", err);
159613a2f6bSGordon Ross }
160613a2f6bSGordon Ross return (0);
161613a2f6bSGordon Ross }
162613a2f6bSGordon Ross
163613a2f6bSGordon Ross
164613a2f6bSGordon Ross int
conn_tcp6(struct smb_ctx * ctx,const struct sockaddr * sa,int port)165613a2f6bSGordon Ross conn_tcp6(struct smb_ctx *ctx, const struct sockaddr *sa, int port)
166613a2f6bSGordon Ross {
167613a2f6bSGordon Ross struct sockaddr_in6 sin6;
168613a2f6bSGordon Ross char *dev = "/dev/tcp6";
169613a2f6bSGordon Ross char paddrbuf[INET6_ADDRSTRLEN];
170613a2f6bSGordon Ross struct t_call sndcall;
171613a2f6bSGordon Ross int fd, err;
172613a2f6bSGordon Ross
173613a2f6bSGordon Ross if (sa->sa_family != AF_INET6) {
174613a2f6bSGordon Ross DPRINT("bad af %d", sa->sa_family);
175613a2f6bSGordon Ross return (EINVAL);
176613a2f6bSGordon Ross }
177613a2f6bSGordon Ross bcopy(sa, &sin6, sizeof (sin6));
178613a2f6bSGordon Ross sin6.sin6_port = htons(port);
179613a2f6bSGordon Ross
180613a2f6bSGordon Ross DPRINT("tcp6: %s (%d)",
181613a2f6bSGordon Ross inet_ntop(AF_INET6, &sin6.sin6_addr,
182613a2f6bSGordon Ross paddrbuf, sizeof (paddrbuf)), port);
183613a2f6bSGordon Ross
184613a2f6bSGordon Ross fd = t_open(dev, O_RDWR, NULL);
185613a2f6bSGordon Ross if (fd < 0) {
186613a2f6bSGordon Ross /* Assume t_errno = TSYSERR */
187613a2f6bSGordon Ross err = errno;
188613a2f6bSGordon Ross perror(dev);
189613a2f6bSGordon Ross return (err);
190613a2f6bSGordon Ross }
191613a2f6bSGordon Ross if ((err = smb_setopts(fd)) != 0)
192613a2f6bSGordon Ross goto errout;
193613a2f6bSGordon Ross if (t_bind(fd, NULL, NULL) < 0) {
194613a2f6bSGordon Ross DPRINT("t_bind t_errno %d", t_errno);
195613a2f6bSGordon Ross if (t_errno == TSYSERR)
196613a2f6bSGordon Ross err = errno;
197613a2f6bSGordon Ross else
198613a2f6bSGordon Ross err = EPROTO;
199613a2f6bSGordon Ross goto errout;
200613a2f6bSGordon Ross }
201613a2f6bSGordon Ross sndcall.addr.maxlen = sizeof (sin6);
202613a2f6bSGordon Ross sndcall.addr.len = sizeof (sin6);
203613a2f6bSGordon Ross sndcall.addr.buf = (void *) &sin6;
204613a2f6bSGordon Ross sndcall.opt.len = 0;
205613a2f6bSGordon Ross sndcall.udata.len = 0;
206613a2f6bSGordon Ross if (t_connect(fd, &sndcall, NULL) < 0) {
207613a2f6bSGordon Ross err = get_xti_err(fd);
208613a2f6bSGordon Ross DPRINT("connect, err %d", err);
209613a2f6bSGordon Ross goto errout;
210613a2f6bSGordon Ross }
211613a2f6bSGordon Ross
212613a2f6bSGordon Ross DPRINT("tcp6: connected, fd=%d", fd);
213613a2f6bSGordon Ross ctx->ct_tran_fd = fd;
214613a2f6bSGordon Ross return (0);
215613a2f6bSGordon Ross
216613a2f6bSGordon Ross errout:
217613a2f6bSGordon Ross close(fd);
218613a2f6bSGordon Ross return (err);
219613a2f6bSGordon Ross }
220613a2f6bSGordon Ross
221613a2f6bSGordon Ross /*
222613a2f6bSGordon Ross * This is used for both SMB over TCP (port 445)
223613a2f6bSGordon Ross * and NetBIOS - see conn_nbt().
224613a2f6bSGordon Ross */
225613a2f6bSGordon Ross int
conn_tcp4(struct smb_ctx * ctx,const struct sockaddr * sa,int port)226613a2f6bSGordon Ross conn_tcp4(struct smb_ctx *ctx, const struct sockaddr *sa, int port)
227613a2f6bSGordon Ross {
228613a2f6bSGordon Ross struct sockaddr_in sin;
229613a2f6bSGordon Ross char *dev = "/dev/tcp";
230613a2f6bSGordon Ross char paddrbuf[INET_ADDRSTRLEN];
231613a2f6bSGordon Ross struct t_call sndcall;
232613a2f6bSGordon Ross int fd, err;
233613a2f6bSGordon Ross
234613a2f6bSGordon Ross if (sa->sa_family != AF_INET) {
235613a2f6bSGordon Ross DPRINT("bad af %d", sa->sa_family);
236613a2f6bSGordon Ross return (EINVAL);
237613a2f6bSGordon Ross }
238613a2f6bSGordon Ross bcopy(sa, &sin, sizeof (sin));
239613a2f6bSGordon Ross sin.sin_port = htons(port);
240613a2f6bSGordon Ross
241613a2f6bSGordon Ross DPRINT("tcp4: %s (%d)",
242613a2f6bSGordon Ross inet_ntop(AF_INET, &sin.sin_addr,
243613a2f6bSGordon Ross paddrbuf, sizeof (paddrbuf)), port);
244613a2f6bSGordon Ross
245613a2f6bSGordon Ross fd = t_open(dev, O_RDWR, NULL);
246613a2f6bSGordon Ross if (fd < 0) {
247613a2f6bSGordon Ross /* Assume t_errno = TSYSERR */
248613a2f6bSGordon Ross err = errno;
249613a2f6bSGordon Ross perror(dev);
250613a2f6bSGordon Ross return (err);
251613a2f6bSGordon Ross }
252613a2f6bSGordon Ross if ((err = smb_setopts(fd)) != 0)
253613a2f6bSGordon Ross goto errout;
254613a2f6bSGordon Ross if (t_bind(fd, NULL, NULL) < 0) {
255613a2f6bSGordon Ross DPRINT("t_bind t_errno %d", t_errno);
256613a2f6bSGordon Ross if (t_errno == TSYSERR)
257613a2f6bSGordon Ross err = errno;
258613a2f6bSGordon Ross else
259613a2f6bSGordon Ross err = EPROTO;
260613a2f6bSGordon Ross goto errout;
261613a2f6bSGordon Ross }
262613a2f6bSGordon Ross sndcall.addr.maxlen = sizeof (sin);
263613a2f6bSGordon Ross sndcall.addr.len = sizeof (sin);
264613a2f6bSGordon Ross sndcall.addr.buf = (void *) &sin;
265613a2f6bSGordon Ross sndcall.opt.len = 0;
266613a2f6bSGordon Ross sndcall.udata.len = 0;
267613a2f6bSGordon Ross if (t_connect(fd, &sndcall, NULL) < 0) {
268613a2f6bSGordon Ross err = get_xti_err(fd);
269613a2f6bSGordon Ross DPRINT("connect, err %d", err);
270613a2f6bSGordon Ross goto errout;
271613a2f6bSGordon Ross }
272613a2f6bSGordon Ross
273613a2f6bSGordon Ross DPRINT("tcp4: connected, fd=%d", fd);
274613a2f6bSGordon Ross ctx->ct_tran_fd = fd;
275613a2f6bSGordon Ross return (0);
276613a2f6bSGordon Ross
277613a2f6bSGordon Ross errout:
278613a2f6bSGordon Ross close(fd);
279613a2f6bSGordon Ross return (err);
280613a2f6bSGordon Ross }
281613a2f6bSGordon Ross
282613a2f6bSGordon Ross /*
283613a2f6bSGordon Ross * Open a NetBIOS connection (session, port 139)
284613a2f6bSGordon Ross *
285613a2f6bSGordon Ross * The optional name parameter, if passed, means
286613a2f6bSGordon Ross * we found the sockaddr via NetBIOS name lookup,
287613a2f6bSGordon Ross * and can just use that for our session request.
288613a2f6bSGordon Ross * Otherwise (if name is NULL), we're connecting
289613a2f6bSGordon Ross * by IP address, and need to come up with the
290613a2f6bSGordon Ross * NetBIOS name by other means.
291613a2f6bSGordon Ross */
292613a2f6bSGordon Ross int
conn_nbt(struct smb_ctx * ctx,const struct sockaddr * saarg,char * name)293613a2f6bSGordon Ross conn_nbt(struct smb_ctx *ctx, const struct sockaddr *saarg, char *name)
294613a2f6bSGordon Ross {
295613a2f6bSGordon Ross struct sockaddr_in sin;
296613a2f6bSGordon Ross struct sockaddr *sa;
297613a2f6bSGordon Ross char server[NB_NAMELEN];
298613a2f6bSGordon Ross char workgroup[NB_NAMELEN];
299613a2f6bSGordon Ross int err, nberr, port;
300613a2f6bSGordon Ross
301613a2f6bSGordon Ross bcopy(saarg, &sin, sizeof (sin));
302613a2f6bSGordon Ross sa = (struct sockaddr *)&sin;
303613a2f6bSGordon Ross
304613a2f6bSGordon Ross switch (sin.sin_family) {
305613a2f6bSGordon Ross case AF_NETBIOS: /* our fake AF */
306613a2f6bSGordon Ross sin.sin_family = AF_INET;
307613a2f6bSGordon Ross break;
308613a2f6bSGordon Ross case AF_INET:
309613a2f6bSGordon Ross break;
310613a2f6bSGordon Ross default:
311613a2f6bSGordon Ross DPRINT("bad af %d", sin.sin_family);
312613a2f6bSGordon Ross return (EINVAL);
313613a2f6bSGordon Ross }
314613a2f6bSGordon Ross port = IPPORT_NETBIOS_SSN;
315613a2f6bSGordon Ross
316613a2f6bSGordon Ross /*
317613a2f6bSGordon Ross * If we have a NetBIOS name, just use it.
318613a2f6bSGordon Ross * This is the path taken when we've done a
319613a2f6bSGordon Ross * NetBIOS name lookup on this name to get
320613a2f6bSGordon Ross * the IP address in the passed sa. Otherwise,
321613a2f6bSGordon Ross * we're connecting by IP address, and need to
322613a2f6bSGordon Ross * figure out what NetBIOS name to use.
323613a2f6bSGordon Ross */
324613a2f6bSGordon Ross if (name) {
325613a2f6bSGordon Ross strlcpy(server, name, sizeof (server));
326613a2f6bSGordon Ross DPRINT("given name: %s", server);
327613a2f6bSGordon Ross } else {
328613a2f6bSGordon Ross /*
329613a2f6bSGordon Ross *
330613a2f6bSGordon Ross * Try a NetBIOS node status query,
331613a2f6bSGordon Ross * which searches for a type=[20] name.
332613a2f6bSGordon Ross * If that doesn't work, just use the
333613a2f6bSGordon Ross * (fake) "*SMBSERVER" name.
334613a2f6bSGordon Ross */
335613a2f6bSGordon Ross DPRINT("try node status");
336613a2f6bSGordon Ross server[0] = '\0';
337613a2f6bSGordon Ross nberr = nbns_getnodestatus(ctx->ct_nb,
338613a2f6bSGordon Ross &sin.sin_addr, server, workgroup);
339613a2f6bSGordon Ross if (nberr == 0 && server[0] != '\0') {
340613a2f6bSGordon Ross /* Found the name. Save for reconnect. */
341613a2f6bSGordon Ross DPRINT("found name: %s", server);
342613a2f6bSGordon Ross strlcpy(ctx->ct_srvname, server,
343613a2f6bSGordon Ross sizeof (ctx->ct_srvname));
344613a2f6bSGordon Ross } else {
345613a2f6bSGordon Ross DPRINT("getnodestatus, nberr %d", nberr);
346613a2f6bSGordon Ross strlcpy(server, "*SMBSERVER", sizeof (server));
347613a2f6bSGordon Ross }
348613a2f6bSGordon Ross }
349613a2f6bSGordon Ross
350613a2f6bSGordon Ross /*
351613a2f6bSGordon Ross * Establish the TCP connection.
352613a2f6bSGordon Ross * Careful to close it on errors.
353613a2f6bSGordon Ross */
354613a2f6bSGordon Ross if ((err = conn_tcp4(ctx, sa, port)) != 0) {
355613a2f6bSGordon Ross DPRINT("TCP connect: err=%d", err);
356613a2f6bSGordon Ross goto out;
357613a2f6bSGordon Ross }
358613a2f6bSGordon Ross
359613a2f6bSGordon Ross /* Connected. Do NetBIOS session request. */
360613a2f6bSGordon Ross err = nb_ssn_request(ctx, server);
361613a2f6bSGordon Ross if (err)
362613a2f6bSGordon Ross DPRINT("ssn_rq, err %d", err);
363613a2f6bSGordon Ross
364613a2f6bSGordon Ross out:
365613a2f6bSGordon Ross if (err) {
366613a2f6bSGordon Ross if (ctx->ct_tran_fd != -1) {
367613a2f6bSGordon Ross close(ctx->ct_tran_fd);
368613a2f6bSGordon Ross ctx->ct_tran_fd = -1;
369613a2f6bSGordon Ross }
370613a2f6bSGordon Ross }
371613a2f6bSGordon Ross return (err);
372613a2f6bSGordon Ross }
373613a2f6bSGordon Ross
374613a2f6bSGordon Ross /*
375613a2f6bSGordon Ross * Make a new connection, or reconnect.
376613a2f6bSGordon Ross */
377613a2f6bSGordon Ross int
smb_iod_connect(smb_ctx_t * ctx)378613a2f6bSGordon Ross smb_iod_connect(smb_ctx_t *ctx)
379613a2f6bSGordon Ross {
380613a2f6bSGordon Ross struct sockaddr *sa;
381613a2f6bSGordon Ross int err, err2;
382613a2f6bSGordon Ross struct mbdata blob;
383613a2f6bSGordon Ross
384613a2f6bSGordon Ross memset(&blob, 0, sizeof (blob));
385613a2f6bSGordon Ross
386613a2f6bSGordon Ross if (ctx->ct_srvname[0] == '\0') {
387613a2f6bSGordon Ross DPRINT("sername not set!");
388613a2f6bSGordon Ross return (EINVAL);
389613a2f6bSGordon Ross }
390613a2f6bSGordon Ross DPRINT("server: %s", ctx->ct_srvname);
391613a2f6bSGordon Ross
392613a2f6bSGordon Ross if (smb_debug)
393613a2f6bSGordon Ross dump_ctx("smb_iod_connect", ctx);
394613a2f6bSGordon Ross
395613a2f6bSGordon Ross /*
396613a2f6bSGordon Ross * This may be a reconnect, so
397613a2f6bSGordon Ross * cleanup if necessary.
398613a2f6bSGordon Ross */
399613a2f6bSGordon Ross if (ctx->ct_tran_fd != -1) {
400613a2f6bSGordon Ross close(ctx->ct_tran_fd);
401613a2f6bSGordon Ross ctx->ct_tran_fd = -1;
402613a2f6bSGordon Ross }
403613a2f6bSGordon Ross
404613a2f6bSGordon Ross /*
405613a2f6bSGordon Ross * Get local machine name.
406613a2f6bSGordon Ross * Full name - not a NetBIOS name.
407613a2f6bSGordon Ross */
408613a2f6bSGordon Ross if (ctx->ct_locname == NULL) {
409613a2f6bSGordon Ross err = smb_getlocalname(&ctx->ct_locname);
410613a2f6bSGordon Ross if (err) {
411613a2f6bSGordon Ross smb_error(dgettext(TEXT_DOMAIN,
412613a2f6bSGordon Ross "can't get local name"), err);
413613a2f6bSGordon Ross return (err);
414613a2f6bSGordon Ross }
415613a2f6bSGordon Ross }
416613a2f6bSGordon Ross
417613a2f6bSGordon Ross /*
418613a2f6bSGordon Ross * We're called with each IP address
419613a2f6bSGordon Ross * already copied into ct_srvaddr.
420613a2f6bSGordon Ross */
421613a2f6bSGordon Ross ctx->ct_flags |= SMBCF_RESOLVED;
422613a2f6bSGordon Ross
423613a2f6bSGordon Ross sa = &ctx->ct_srvaddr.sa;
424613a2f6bSGordon Ross switch (sa->sa_family) {
425613a2f6bSGordon Ross
426613a2f6bSGordon Ross case AF_INET6:
427613a2f6bSGordon Ross err = conn_tcp6(ctx, sa, IPPORT_SMB);
428613a2f6bSGordon Ross break;
429613a2f6bSGordon Ross
430613a2f6bSGordon Ross case AF_INET:
431613a2f6bSGordon Ross err = conn_tcp4(ctx, sa, IPPORT_SMB);
432613a2f6bSGordon Ross /*
433613a2f6bSGordon Ross * If port 445 was not listening, try port 139.
434613a2f6bSGordon Ross * Note: Not doing NetBIOS name lookup here.
435613a2f6bSGordon Ross * We already have the IP address.
436613a2f6bSGordon Ross */
437613a2f6bSGordon Ross switch (err) {
438613a2f6bSGordon Ross case ECONNRESET:
439613a2f6bSGordon Ross case ECONNREFUSED:
440613a2f6bSGordon Ross err2 = conn_nbt(ctx, sa, NULL);
441613a2f6bSGordon Ross if (err2 == 0)
442613a2f6bSGordon Ross err = 0;
443613a2f6bSGordon Ross }
444613a2f6bSGordon Ross break;
445613a2f6bSGordon Ross
446613a2f6bSGordon Ross case AF_NETBIOS:
447613a2f6bSGordon Ross /* Like AF_INET, but use NetBIOS ssn. */
448613a2f6bSGordon Ross err = conn_nbt(ctx, sa, ctx->ct_srvname);
449613a2f6bSGordon Ross break;
450613a2f6bSGordon Ross
451613a2f6bSGordon Ross default:
452613a2f6bSGordon Ross DPRINT("skipped family %d", sa->sa_family);
453613a2f6bSGordon Ross err = EPROTONOSUPPORT;
454613a2f6bSGordon Ross break;
455613a2f6bSGordon Ross }
456613a2f6bSGordon Ross
457613a2f6bSGordon Ross
458613a2f6bSGordon Ross if (err) {
459613a2f6bSGordon Ross DPRINT("connect, err=%d", err);
460613a2f6bSGordon Ross return (err);
461613a2f6bSGordon Ross }
462613a2f6bSGordon Ross
463613a2f6bSGordon Ross /*
464613a2f6bSGordon Ross * Do SMB Negotiate Protocol.
465613a2f6bSGordon Ross */
466613a2f6bSGordon Ross err = smb_negprot(ctx, &blob);
467613a2f6bSGordon Ross if (err)
468613a2f6bSGordon Ross goto out;
469613a2f6bSGordon Ross
470613a2f6bSGordon Ross /*
471*85e6b674SGordon Ross * Empty user name means an explicit request for
472*85e6b674SGordon Ross * NULL session setup, which is a special case.
473*85e6b674SGordon Ross * If negotiate determined that we want to do
474*85e6b674SGordon Ross * SMB signing, we have to turn that off for a
475*85e6b674SGordon Ross * NULL session. [MS-SMB 3.3.5.3].
476*85e6b674SGordon Ross */
477*85e6b674SGordon Ross if (ctx->ct_user[0] == '\0') {
478*85e6b674SGordon Ross /* Null user should have null domain too. */
479*85e6b674SGordon Ross ctx->ct_domain[0] = '\0';
480*85e6b674SGordon Ross ctx->ct_authflags = SMB_AT_ANON;
481*85e6b674SGordon Ross ctx->ct_clnt_caps &= ~SMB_CAP_EXT_SECURITY;
482*85e6b674SGordon Ross ctx->ct_vcflags &= ~SMBV_WILL_SIGN;
483*85e6b674SGordon Ross }
484*85e6b674SGordon Ross
485*85e6b674SGordon Ross /*
486613a2f6bSGordon Ross * Do SMB Session Setup (authenticate)
487613a2f6bSGordon Ross *
488613a2f6bSGordon Ross * If the server negotiated extended security,
489*85e6b674SGordon Ross * run the SPNEGO state machine, otherwise do
490*85e6b674SGordon Ross * one of the old-style variants.
491613a2f6bSGordon Ross */
492*85e6b674SGordon Ross if (ctx->ct_clnt_caps & SMB_CAP_EXT_SECURITY) {
493613a2f6bSGordon Ross err = smb_ssnsetup_spnego(ctx, &blob);
494613a2f6bSGordon Ross } else {
495613a2f6bSGordon Ross /*
496613a2f6bSGordon Ross * Server did NOT negotiate extended security.
497*85e6b674SGordon Ross * Try NTLMv2, NTLMv1, or ANON (if enabled).
498613a2f6bSGordon Ross */
499*85e6b674SGordon Ross if (ctx->ct_authflags & SMB_AT_NTLM2) {
500*85e6b674SGordon Ross err = smb_ssnsetup_ntlm2(ctx);
501*85e6b674SGordon Ross } else if (ctx->ct_authflags & SMB_AT_NTLM1) {
502*85e6b674SGordon Ross err = smb_ssnsetup_ntlm1(ctx);
503*85e6b674SGordon Ross } else if (ctx->ct_authflags & SMB_AT_ANON) {
504*85e6b674SGordon Ross err = smb_ssnsetup_null(ctx);
505*85e6b674SGordon Ross } else {
506613a2f6bSGordon Ross /*
507*85e6b674SGordon Ross * Don't return EAUTH, because a new
508*85e6b674SGordon Ross * password prompt will not help.
509613a2f6bSGordon Ross */
510613a2f6bSGordon Ross DPRINT("No NTLM authflags");
511613a2f6bSGordon Ross err = ENOTSUP;
512613a2f6bSGordon Ross }
513613a2f6bSGordon Ross }
514613a2f6bSGordon Ross
515613a2f6bSGordon Ross out:
516613a2f6bSGordon Ross mb_done(&blob);
517613a2f6bSGordon Ross
518613a2f6bSGordon Ross if (err) {
519613a2f6bSGordon Ross close(ctx->ct_tran_fd);
520613a2f6bSGordon Ross ctx->ct_tran_fd = -1;
521*85e6b674SGordon Ross } else {
522*85e6b674SGordon Ross /* Tell library code we have a session. */
523*85e6b674SGordon Ross ctx->ct_flags |= SMBCF_SSNACTIVE;
524613a2f6bSGordon Ross DPRINT("tran_fd = %d", ctx->ct_tran_fd);
525*85e6b674SGordon Ross }
526613a2f6bSGordon Ross
527613a2f6bSGordon Ross return (err);
528613a2f6bSGordon Ross }
529