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 * 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 *
smb_iod_state_name(enum smbiod_state st)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
smb_iod_connect(smb_ctx_t * ctx)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
smb_ssnsetup_spnego(struct smb_ctx * ctx,struct mbdata * hint_mb)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 session key is in ctx->ct_ssnkey_buf
334 * (a.k.a. ct_work.wk_u_ssn_key_buf)
335 */
336
337 out:
338 /* Done with ctx->ct_ssp_ctx */
339 ssp_ctx_destroy(ctx);
340
341 return (err);
342 }
343
344 int smb_max_authtok_sz = 0x10000;
345
346 /*
347 * Session Setup function, calling the nsmb driver.
348 *
349 * Args
350 * send_mb: [in] outgoing blob data to send
351 * recv_mb: [out] received blob data buffer
352 */
353 static int
smb__ssnsetup(struct smb_ctx * ctx,struct mbdata * send_mb,struct mbdata * recv_mb)354 smb__ssnsetup(struct smb_ctx *ctx,
355 struct mbdata *send_mb, struct mbdata *recv_mb)
356 {
357 smbioc_ossn_t *ossn = &ctx->ct_ssn;
358 smbioc_ssn_work_t *work = &ctx->ct_work;
359 mbuf_t *m;
360 int err;
361
362 /* Setup receive buffer for the auth data. */
363 err = mb_init_sz(recv_mb, smb_max_authtok_sz);
364 if (err != 0)
365 return (err);
366 m = recv_mb->mb_top;
367 work->wk_u_auth_rbuf.lp_ptr = m->m_data;
368 work->wk_u_auth_rlen = m->m_maxlen;
369
370 /* ... and the auth data to send. */
371 m = send_mb->mb_top;
372 work->wk_u_auth_wbuf.lp_ptr = m->m_data;
373 work->wk_u_auth_wlen = m->m_len;
374
375 DPRINT("Session setup ioctl...");
376 if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_IOD_SSNSETUP, work) < 0) {
377 err = errno;
378 if (err != 0 && err != EINPROGRESS) {
379 smb_error(dgettext(TEXT_DOMAIN,
380 "%s: session setup "),
381 err, ossn->ssn_srvname);
382 }
383 }
384 DPRINT("Session setup ret %d", err);
385
386 /* Free the auth data we sent. */
387 mb_done(send_mb);
388
389 /* Setup length of received auth data */
390 m = recv_mb->mb_top;
391 m->m_len = work->wk_u_auth_rlen;
392
393 return (err);
394 }
395