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 *
  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 }