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 }
|
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 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
27 */
28
29 /*
30 * Functions to setup connections (TCP and/or NetBIOS)
31 * This has the fall-back logic for IP6, IP4, NBT
32 */
33
34 #include <errno.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <netdb.h>
41 #include <libintl.h>
42 #include <xti.h>
43 #include <assert.h>
44
45 #include <sys/types.h>
46 #include <sys/time.h>
47 #include <sys/byteorder.h>
48 #include <sys/socket.h>
49 #include <sys/fcntl.h>
50
51 #include <netinet/in.h>
52 #include <netinet/tcp.h>
53 #include <arpa/inet.h>
54 #include <uuid/uuid.h>
55
56 #include <netsmb/smb.h>
57 #include <netsmb/smb_lib.h>
58 #include <netsmb/mchain.h>
59 #include <netsmb/netbios.h>
60 #include <netsmb/nb_lib.h>
61 #include <netsmb/smb_dev.h>
62
63 #include <cflib.h>
64
65 #include "charsets.h"
66 #include "private.h"
67 #include "smb_crypt.h"
68
69 static int
70 smb__ssnsetup(struct smb_ctx *ctx,
71 struct mbdata *mbc1, struct mbdata *mbc2);
72
73 int smb_ssnsetup_spnego(struct smb_ctx *, struct mbdata *);
74
75 const char *
76 smb_iod_state_name(enum smbiod_state st)
77 {
78 const char *n = "(?)";
79
80 switch (st) {
81 case SMBIOD_ST_UNINIT:
82 n = "UNINIT!";
83 break;
84 case SMBIOD_ST_IDLE:
85 n = "IDLE";
86 break;
87 case SMBIOD_ST_RECONNECT:
88 n = "RECONNECT";
89 break;
90 case SMBIOD_ST_RCFAILED:
91 n = "RCFAILED";
92 break;
93 case SMBIOD_ST_CONNECTED:
94 n = "CONNECTED";
95 break;
96 case SMBIOD_ST_NEGOTIATED:
97 n = "NEGOTIATED";
98 break;
99 case SMBIOD_ST_AUTHCONT:
100 n = "AUTHCONT";
101 break;
102 case SMBIOD_ST_AUTHFAIL:
103 n = "AUTHFAIL";
104 break;
105 case SMBIOD_ST_AUTHOK:
106 n = "AUTHOK";
107 break;
108 case SMBIOD_ST_VCACTIVE:
109 n = "VCACTIVE";
110 break;
111 case SMBIOD_ST_DEAD:
112 n = "DEAD";
113 break;
114 }
115
116 return (n);
117 }
118
119 /*
120 * Make a new connection, or reconnect.
121 *
122 * This is called first from the door service thread in smbiod
123 * (so that can report success or failure to the door client)
124 * and thereafter it's called when we need to reconnect after a
125 * network outage (or whatever might cause connection loss).
126 */
127 int
128 smb_iod_connect(smb_ctx_t *ctx)
129 {
130 smbioc_ossn_t *ossn = &ctx->ct_ssn;
131 smbioc_ssn_work_t *work = &ctx->ct_work;
132 char *uuid_str;
133 int err;
134 struct mbdata blob;
135 char *nego_buf = NULL;
136 uint32_t nego_len;
137
138 memset(&blob, 0, sizeof (blob));
139
140 if (ctx->ct_srvname[0] == '\0') {
141 DPRINT("sername not set!");
142 return (EINVAL);
143 }
144 DPRINT("server: %s", ctx->ct_srvname);
145
146 if (smb_debug)
147 dump_ctx("smb_iod_connect", ctx);
148
149 /*
150 * Get local machine name.
151 * Full name - not a NetBIOS name.
152 */
153 if (ctx->ct_locname == NULL) {
154 err = smb_getlocalname(&ctx->ct_locname);
155 if (err) {
156 smb_error(dgettext(TEXT_DOMAIN,
157 "can't get local name"), err);
158 return (err);
159 }
160 }
161
162 /*
163 * Get local machine uuid.
164 */
165 uuid_str = cf_get_client_uuid();
166 if (uuid_str == NULL) {
167 err = EINVAL;
168 smb_error(dgettext(TEXT_DOMAIN,
169 "can't get local UUID"), err);
170 return (err);
171 }
172 (void) uuid_parse(uuid_str, ctx->ct_work.wk_cl_guid);
173 free(uuid_str);
174 uuid_str = NULL;
175
176 /*
177 * We're called with each IP address
178 * already copied into ct_srvaddr.
179 */
180 ctx->ct_flags |= SMBCF_RESOLVED;
181
182 /*
183 * Ask the drvier to connect.
184 */
185 DPRINT("Try ioctl connect...");
186 if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_IOD_CONNECT, work) < 0) {
187 err = errno;
188 smb_error(dgettext(TEXT_DOMAIN,
189 "%s: connect failed"),
190 err, ossn->ssn_srvname);
191 return (err);
192 }
193 DPRINT("Connect OK, new state=%s",
194 smb_iod_state_name(work->wk_out_state));
195
196 /*
197 * Setup a buffer to recv the nego. hint.
198 */
199 nego_len = 4096;
200 err = mb_init_sz(&blob, nego_len);
201 if (err)
202 goto out;
203 nego_buf = blob.mb_top->m_data;
204 work->wk_u_auth_rbuf.lp_ptr = nego_buf;
205 work->wk_u_auth_rlen = nego_len;
206
207 /*
208 * Ask the driver for SMB negotiate
209 */
210 DPRINT("Try ioctl negotiate...");
211 if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_IOD_NEGOTIATE, work) < 0) {
212 err = errno;
213 smb_error(dgettext(TEXT_DOMAIN,
214 "%s: negotiate failed"),
215 err, ossn->ssn_srvname);
216 goto out;
217 }
218 DPRINT("Negotiate OK, new state=%s",
219 smb_iod_state_name(work->wk_out_state));
220
221 nego_len = work->wk_u_auth_rlen;
222 blob.mb_top->m_len = nego_len;
223
224 if (smb_debug) {
225 DPRINT("Sec. blob: %d", nego_len);
226 smb_hexdump(nego_buf, nego_len);
227 }
228
229 /*
230 * Do SMB Session Setup (authenticate)
231 * Always "extended security" now (SPNEGO)
232 */
233 DPRINT("Do session setup...");
234 err = smb_ssnsetup_spnego(ctx, &blob);
235 if (err != 0) {
236 DPRINT("Session setup err=%d", err);
237 goto out;
238 }
239
240 /*
241 * Success! We return zero now, and our caller (normally
242 * the smbiod program) will then call smb_iod_work in a
243 * new thread to service this VC as long as necessary.
244 */
245 DPRINT("Session setup OK");
246
247 out:
248 mb_done(&blob);
249
250 return (err);
251 }
252
253 /*
254 * smb_ssnsetup_spnego
255 *
256 * This does an SMB session setup sequence using SPNEGO.
257 * The state changes seen during this sequence are there
258 * just to help track what's going on.
259 */
260 int
261 smb_ssnsetup_spnego(struct smb_ctx *ctx, struct mbdata *hint_mb)
262 {
263 struct mbdata send_mb, recv_mb;
264 smbioc_ssn_work_t *work = &ctx->ct_work;
265 int err;
266
267 bzero(&send_mb, sizeof (send_mb));
268 bzero(&recv_mb, sizeof (recv_mb));
269
270 err = ssp_ctx_create_client(ctx, hint_mb);
271 if (err)
272 goto out;
273
274 /* NULL input indicates first call. */
275 err = ssp_ctx_next_token(ctx, NULL, &send_mb);
276 if (err) {
277 DPRINT("smb__ssnsetup, ssp next, err=%d", err);
278 goto out;
279 }
280 for (;;) {
281 err = smb__ssnsetup(ctx, &send_mb, &recv_mb);
282 DPRINT("smb__ssnsetup rc=%d, new state=%s", err,
283 smb_iod_state_name(work->wk_out_state));
284
285 if (err == 0) {
286 /*
287 * Session setup complete w/ success.
288 * Should have state AUTHOK
289 */
290 if (work->wk_out_state != SMBIOD_ST_AUTHOK) {
291 DPRINT("Wrong state (expected AUTHOK)");
292 }
293 break;
294 }
295
296 if (err != EINPROGRESS) {
297 /*
298 * Session setup complete w/ failure.
299 * Should have state AUTHFAIL
300 */
301 if (work->wk_out_state != SMBIOD_ST_AUTHFAIL) {
302 DPRINT("Wrong state (expected AUTHFAIL)");
303 }
304 goto out;
305 }
306
307 /*
308 * err == EINPROGRESS
309 * Session setup continuing.
310 * Should have state AUTHCONT
311 */
312 if (work->wk_out_state != SMBIOD_ST_AUTHCONT) {
313 DPRINT("Wrong state (expected AUTHCONT)");
314 }
315
316 /* middle calls get both in, out */
317 err = ssp_ctx_next_token(ctx, &recv_mb, &send_mb);
318 if (err) {
319 DPRINT("smb__ssnsetup, ssp next, err=%d", err);
320 goto out;
321 }
322 }
323
324 /*
325 * Only get here via break in the err==0 case above,
326 * so we're finalizing a successful session setup.
327 *
328 * NULL output token here indicates the final call.
329 */
330 (void) ssp_ctx_next_token(ctx, &recv_mb, NULL);
331
332 /*
333 * The signing key is in ctx->ct_mackey
334 * (a.k.a. ct_work.wk_iods.is_u_mackey)
335 * and session key is in ctx->ct_ssn_key
336 * (a.k.a. ct_work.wk_iods.is_ssn_key)
337 */
338
339 out:
340 /* Done with ctx->ct_ssp_ctx */
341 ssp_ctx_destroy(ctx);
342
343 return (err);
344 }
345
346 int smb_max_authtok_sz = 0x10000;
347
348 /*
349 * Session Setup function, calling the nsmb driver.
350 *
351 * Args
352 * send_mb: [in] outgoing blob data to send
353 * recv_mb: [out] received blob data buffer
354 */
355 static int
356 smb__ssnsetup(struct smb_ctx *ctx,
357 struct mbdata *send_mb, struct mbdata *recv_mb)
358 {
359 smbioc_ossn_t *ossn = &ctx->ct_ssn;
360 smbioc_ssn_work_t *work = &ctx->ct_work;
361 mbuf_t *m;
362 int err;
363
364 /* Setup receive buffer for the auth data. */
365 err = mb_init_sz(recv_mb, smb_max_authtok_sz);
366 if (err != 0)
367 return (err);
368 m = recv_mb->mb_top;
369 work->wk_u_auth_rbuf.lp_ptr = m->m_data;
370 work->wk_u_auth_rlen = m->m_maxlen;
371
372 /* ... and the auth data to send. */
373 m = send_mb->mb_top;
374 work->wk_u_auth_wbuf.lp_ptr = m->m_data;
375 work->wk_u_auth_wlen = m->m_len;
376
377 DPRINT("Session setup ioctl...");
378 if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_IOD_SSNSETUP, work) < 0) {
379 err = errno;
380 if (err != 0 && err != EINPROGRESS) {
381 smb_error(dgettext(TEXT_DOMAIN,
382 "%s: session setup "),
383 err, ossn->ssn_srvname);
384 }
385 }
386 DPRINT("Session setup ret %d", err);
387
388 /* Free the auth data we sent. */
389 mb_done(send_mb);
390
391 /* Setup length of received auth data */
392 m = recv_mb->mb_top;
393 m->m_len = work->wk_u_auth_rlen;
394
395 return (err);
396 }
|