Print this page
NEX-19781 SMB service shutdown hang with threads in smb_authsock_recv
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-16604 Windows 10 SMB client exhausts smbauth sockets
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-13644 File access audit logging
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-9497 SMB should bypass ACL traverse checking
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-10231 SMB logon fails in fksmbd
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-5273 SMB 3 Encryption
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-7267 Codenomicon causes panic in smb_authsock_cancel (lint)
NEX-7267 Codenomicon causes panic in smb_authsock_cancel
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-6888 Defensics test causes panic due to bad free
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-5560 smb2 should use 64-bit server-global uids
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-2522 svcadm disable network/smb/server may hang
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-3610 CLONE NEX-3591 SMB3 signing
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
NEX-3611 CLONE NEX-3550 Replace smb2_enable with max_protocol
Reviewed by: Yuri Pankov <Yuri.Pankov@nexenta.com>
NEX-3080 SMB1 signing problem with Kerberos auth.
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-2485 SMB authentication flood handled poorly
SUP-866 smbd lwps stuck in libsocket recv() for no apparent reason
SMB-152 mem leak in smb_authenticate_ext/smb_authsock_sendrecv
SMB-55 SMB2 signing
OS-6 mpathadm leads to panic in vhci_mpapi_sync_lu_oid_list
SMB-85 Codenomicon: SMB2 TC: 16193 - Panic in smb_authenticate_ext
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-56 extended security NTLMSSP, inbound

@@ -18,11 +18,11 @@
  *
  * CDDL HEADER END
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * Authentication support for SMB session setup
  */

@@ -35,14 +35,14 @@
 #include <netinet/in.h>
 #include <smbsrv/smb_idmap.h>
 #include <smbsrv/smb_kproto.h>
 #include <smbsrv/smb_token.h>
 
-static uint32_t smb_authsock_open(smb_user_t *);
+static uint32_t smb_authsock_open(smb_request_t *);
 static int smb_authsock_send(ksocket_t, void *, size_t);
 static int smb_authsock_recv(ksocket_t, void *, size_t);
-static uint32_t smb_authsock_sendrecv(smb_user_t *, smb_lsa_msg_hdr_t *hdr,
+static uint32_t smb_authsock_sendrecv(smb_request_t *, smb_lsa_msg_hdr_t *hdr,
                                 void *sndbuf, void **recvbuf);
 /* void smb_authsock_close(smb_user_t *); kproto.h */
 
 static uint32_t smb_auth_do_clinfo(smb_request_t *);
 static uint32_t smb_auth_do_oldreq(smb_request_t *);

@@ -49,10 +49,11 @@
 static uint32_t smb_auth_get_token(smb_request_t *);
 static uint32_t smb_priv_xlate(smb_token_t *);
 
 /*
  * Handle old-style session setup (non-extended security)
+ * Note: Used only by SMB1
  *
  * The user information is passed to smbd for authentication.
  * If smbd can authenticate the user an access token is returned and we
  * generate a cred and new user based on the token.
  */

@@ -67,17 +68,18 @@
                 return (NT_STATUS_TOO_MANY_SESSIONS);
 
         /* user cleanup in smb_request_free */
         sr->uid_user = user;
         sr->smb_uid = user->u_uid;
+        sr->smb2_ssnid = 0;
 
         /*
          * Open a connection to the local logon service.
          * If we can't, it may be busy, or not running.
          * Don't log here - this may be frequent.
          */
-        if ((status = smb_authsock_open(user)) != 0)
+        if ((status = smb_authsock_open(sr)) != 0)
                 goto errout;
 
         /*
          * Tell the auth. svc who this client is.
          */

@@ -112,11 +114,10 @@
 {
         smb_lsa_msg_hdr_t       msg_hdr;
         smb_logon_t     user_info;
         XDR             xdrs;
         smb_arg_sessionsetup_t *sinfo = sr->sr_ssetup;
-        smb_user_t      *user = sr->uid_user;
         void            *sbuf = NULL;
         void            *rbuf = NULL;
         uint32_t        slen = 0;
         uint32_t        rlen = 0;
         uint32_t        status;

@@ -151,11 +152,11 @@
                 goto out;
         }
 
         msg_hdr.lmh_msgtype = LSA_MTYPE_OLDREQ;
         msg_hdr.lmh_msglen = slen;
-        status = smb_authsock_sendrecv(user, &msg_hdr, sbuf, &rbuf);
+        status = smb_authsock_sendrecv(sr, &msg_hdr, sbuf, &rbuf);
         if (status != 0)
                 goto out;
         rlen = msg_hdr.lmh_msglen;
         kmem_free(sbuf, slen);
         sbuf = NULL;

@@ -230,31 +231,53 @@
         uint32_t        status;
 
         ASSERT(sr->uid_user == NULL);
 
         /*
-         * On the first request (UID==0) create a USER object.
-         * On subsequent requests (UID!=0) find the USER object.
+         * Paranoid:  While finding/creating the user object, make sure
+         * SMB2 ignores smb_uid, and SMB1 ignores smb2_ssnid.  The
+         * logic below assumes the "other" one is always zero; both
+         * the "first request" tests and smb_session_lookup_uid_st.
+         */
+        if (sr->session->dialect >= SMB_VERS_2_BASE) {
+                /* SMB2+ ignores smb_uid */
+                ASSERT(sr->smb_uid == 0);
+                sr->smb_uid = 0;
+        } else {
+                /* SMB1 ignores smb2_ssnid */
+                ASSERT(sr->smb2_ssnid == 0);
+                sr->smb2_ssnid = 0;
+        }
+
+        /*
+         * On the first request (UID/ssnid==0) create a USER object.
+         * On subsequent requests (UID/ssnid!=0) find the USER object.
          * Either way, sr->uid_user is set, so our ref. on the
          * user object is dropped during normal cleanup work
          * for the smb_request (sr).  Ditto u_authsock.
          */
-        if (sr->smb_uid == 0) {
+        if (sr->smb2_ssnid == 0 && sr->smb_uid == 0) {
                 user = smb_user_new(sr->session);
                 if (user == NULL)
                         return (NT_STATUS_TOO_MANY_SESSIONS);
 
                 /* user cleanup in smb_request_free */
                 sr->uid_user = user;
+                if (sr->session->dialect >= SMB_VERS_2_BASE) {
+                        /* Intentionally leave smb_uid=0 for SMB2 */
+                        sr->smb2_ssnid = user->u_ssnid;
+                } else {
+                        /* Intentionally leave smb2_ssnid=0 for SMB1 */
                 sr->smb_uid = user->u_uid;
+                }
 
                 /*
                  * Open a connection to the local logon service.
                  * If we can't, it may be busy, or not running.
                  * Don't log here - this may be frequent.
                  */
-                if ((status = smb_authsock_open(user)) != 0)
+                if ((status = smb_authsock_open(sr)) != 0)
                         goto errout;
 
                 /*
                  * Tell the auth. svc who this client is.
                  */

@@ -262,11 +285,11 @@
                         goto errout;
 
                 msg_hdr.lmh_msgtype = LSA_MTYPE_ESFIRST;
         } else {
                 user = smb_session_lookup_uid_st(sr->session,
-                    sr->smb_uid, SMB_USER_STATE_LOGGING_ON);
+                    sr->smb2_ssnid, sr->smb_uid, SMB_USER_STATE_LOGGING_ON);
                 if (user == NULL)
                         return (NT_STATUS_USER_SESSION_DELETED);
 
                 /* user cleanup in smb_request_free */
                 sr->uid_user = user;

@@ -278,11 +301,11 @@
          * Wrap the "security blob" with our header
          * (LSA_MTYPE_ESFIRST or LSA_MTYPE_ESNEXT)
          * and send it up the authsock with either
          */
         msg_hdr.lmh_msglen = sinfo->ssi_iseclen;
-        status = smb_authsock_sendrecv(user, &msg_hdr,
+        status = smb_authsock_sendrecv(sr, &msg_hdr,
             sinfo->ssi_isecblob, &rbuf);
         if (status != 0)
                 goto errout;
         rlen = msg_hdr.lmh_msglen;
 

@@ -348,11 +371,10 @@
 static uint32_t
 smb_auth_do_clinfo(smb_request_t *sr)
 {
         smb_lsa_msg_hdr_t msg_hdr;
         smb_lsa_clinfo_t clinfo;
-        smb_user_t *user = sr->uid_user;
         void *rbuf = NULL;
         uint32_t status;
 
         /*
          * Send a message with info. about the client

@@ -362,11 +384,11 @@
         msg_hdr.lmh_msglen = sizeof (clinfo);
         clinfo.lci_clnt_ipaddr = sr->session->ipaddr;
         (void) memcpy(clinfo.lci_challenge_key,
             sr->session->challenge_key,
             sizeof (clinfo.lci_challenge_key));
-        status = smb_authsock_sendrecv(user, &msg_hdr, &clinfo, &rbuf);
+        status = smb_authsock_sendrecv(sr, &msg_hdr, &clinfo, &rbuf);
         /* We don't use this response. */
         if (rbuf != NULL) {
                 kmem_free(rbuf, msg_hdr.lmh_msglen);
                 rbuf = NULL;
         }

@@ -388,17 +410,16 @@
         cred_t          *cr = NULL;
         void            *rbuf = NULL;
         uint32_t        rlen = 0;
         uint32_t        privileges;
         uint32_t        status;
-        int             rc;
         bool_t          ok;
 
         msg_hdr.lmh_msgtype = LSA_MTYPE_GETTOK;
         msg_hdr.lmh_msglen = 0;
 
-        status = smb_authsock_sendrecv(user, &msg_hdr, NULL, &rbuf);
+        status = smb_authsock_sendrecv(sr, &msg_hdr, NULL, &rbuf);
         if (status != 0)
                 goto errout;
 
         rlen = msg_hdr.lmh_msglen;
         switch (msg_hdr.lmh_msgtype) {

@@ -435,34 +456,46 @@
         rbuf = NULL;
 
         /*
          * Setup the logon object.
          */
-        cr = smb_cred_create(token);
+        cr = smb_cred_create(token, sr->session);
         if (cr == NULL)
                 goto errout;
         privileges = smb_priv_xlate(token);
         (void) smb_user_logon(user, cr,
             token->tkn_domain_name, token->tkn_account_name,
             token->tkn_flags, privileges, token->tkn_audit_sid);
         crfree(cr);
 
         /*
+         * Some basic processing for encryption needs to be done,
+         * even for anonymous/guest sessions. In particular,
+         * we need to set Session.EncryptData.
+         *
+         * Windows handling of anon/guest and encryption is strange.
+         * It allows these accounts to get through session setup,
+         * even when they provide no key material.
+         * Additionally, Windows somehow manages to have key material
+         * for anonymous accounts under unknown circumstances.
+         * As such, We set EncryptData on anon/guest to behave like Windows,
+         * at least through Session Setup.
+         */
+        if (sr->session->dialect >= SMB_VERS_3_0)
+                smb3_encrypt_begin(sr, token);
+
+        /*
          * Save the session key, and (maybe) enable signing,
          * but only for real logon (not ANON or GUEST).
          */
         if ((token->tkn_flags & (SMB_ATF_GUEST | SMB_ATF_ANON)) == 0) {
                 if (sr->session->dialect >= SMB_VERS_2_BASE) {
-                        rc = smb2_sign_begin(sr, token);
+                        smb2_sign_begin(sr, token);
                 } else {
-                        rc = smb_sign_begin(sr, token);
+                        smb_sign_begin(sr, token);
                 }
-                if (rc != 0) {
-                        status = NT_STATUS_INTERNAL_ERROR;
-                        goto errout;
                 }
-        }
 
         smb_token_free(token);
 
         sr->user_cr = user->u_cred;
         return (0);

@@ -494,36 +527,71 @@
 static uint32_t
 smb_priv_xlate(smb_token_t *token)
 {
         uint32_t        privileges = 0;
 
+        if (smb_token_query_privilege(token, SE_SECURITY_LUID))
+                privileges |= SMB_USER_PRIV_SECURITY;
+
+        if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID))
+                privileges |= SMB_USER_PRIV_TAKE_OWNERSHIP;
+
         if (smb_token_query_privilege(token, SE_BACKUP_LUID))
                 privileges |= SMB_USER_PRIV_BACKUP;
 
         if (smb_token_query_privilege(token, SE_RESTORE_LUID))
                 privileges |= SMB_USER_PRIV_RESTORE;
 
-        if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID))
-                privileges |= SMB_USER_PRIV_TAKE_OWNERSHIP;
+        if (smb_token_query_privilege(token, SE_CHANGE_NOTIFY_LUID))
+                privileges |= SMB_USER_PRIV_CHANGE_NOTIFY;
 
-        if (smb_token_query_privilege(token, SE_SECURITY_LUID))
-                privileges |= SMB_USER_PRIV_SECURITY;
-
         return (privileges);
 }
 
 /*
+ * Unblock a request that might be blocked reading some
+ * authentication socket.  This can happen when either the
+ * client cancels a session setup or closes the connection.
+ */
+static void
+smb_authsock_cancel(smb_request_t *sr)
+{
+        smb_user_t *user = sr->cancel_arg2;
+        ksocket_t authsock = NULL;
+
+        if (user == NULL)
+                return;
+        ASSERT(user == sr->uid_user);
+
+        /*
+         * Check user state, and get a hold on the auth socket.
+         */
+        mutex_enter(&user->u_mutex);
+        if (user->u_state == SMB_USER_STATE_LOGGING_ON) {
+                if ((authsock = user->u_authsock) != NULL)
+                        ksocket_hold(authsock);
+        }
+        mutex_exit(&user->u_mutex);
+
+        if (authsock != NULL) {
+                (void) ksocket_shutdown(authsock, SHUT_RDWR, sr->user_cr);
+                ksocket_rele(authsock);
+        }
+}
+
+/*
  * Send/recv a request/reply sequence on the auth socket.
  * Returns zero or an NT status.
  *
  * Errors here mean we can't communicate with the smbd_authsvc.
  * With limited authsock instances, this should be rare.
  */
 static uint32_t
-smb_authsock_sendrecv(smb_user_t *user, smb_lsa_msg_hdr_t *hdr,
+smb_authsock_sendrecv(smb_request_t *sr, smb_lsa_msg_hdr_t *hdr,
         void *sndbuf, void **recvbuf)
 {
+        smb_user_t *user = sr->uid_user;
         ksocket_t so;
         uint32_t status;
         int rc;
 
         /*

@@ -536,29 +604,32 @@
                 return (NT_STATUS_INTERNAL_ERROR);
         }
         ksocket_hold(so);
         mutex_exit(&user->u_mutex);
 
+        mutex_enter(&sr->sr_mutex);
+        if (sr->sr_state != SMB_REQ_STATE_ACTIVE) {
+                mutex_exit(&sr->sr_mutex);
+                status = NT_STATUS_CANCELLED;
+                goto out;
+        }
+        sr->sr_state = SMB_REQ_STATE_WAITING_AUTH;
+        sr->cancel_method = smb_authsock_cancel;
+        sr->cancel_arg2 = user;
+        mutex_exit(&sr->sr_mutex);
+
         rc = smb_authsock_send(so, hdr, sizeof (*hdr));
         if (rc == 0 && hdr->lmh_msglen != 0) {
                 rc = smb_authsock_send(so, sndbuf, hdr->lmh_msglen);
         }
-        if (rc)
-                goto out;
-
+        if (rc == 0)
         rc = smb_authsock_recv(so, hdr, sizeof (*hdr));
         if (rc == 0 && hdr->lmh_msglen != 0) {
                 *recvbuf = kmem_alloc(hdr->lmh_msglen, KM_SLEEP);
                 rc = smb_authsock_recv(so, *recvbuf, hdr->lmh_msglen);
-                if (rc) {
-                        kmem_free(*recvbuf, hdr->lmh_msglen);
-                        *recvbuf = NULL;
                 }
-        }
 
-out:
-        ksocket_rele(so);
         switch (rc) {
         case 0:
                 status = 0;
                 break;
         case EIO:

@@ -570,10 +641,34 @@
         default:
                 status = RPC_NT_CALL_FAILED;
                 break;
         }
 
+        mutex_enter(&sr->sr_mutex);
+        sr->cancel_method = NULL;
+        sr->cancel_arg2 = NULL;
+        switch (sr->sr_state) {
+        case SMB_REQ_STATE_WAITING_AUTH:
+                sr->sr_state = SMB_REQ_STATE_ACTIVE;
+                break;
+        case SMB_REQ_STATE_CANCEL_PENDING:
+                sr->sr_state = SMB_REQ_STATE_CANCELLED;
+                status = NT_STATUS_CANCELLED;
+                break;
+        default:
+                status = NT_STATUS_INTERNAL_ERROR;
+                break;
+        }
+        mutex_exit(&sr->sr_mutex);
+
+out:
+        ksocket_rele(so);
+
+        if (status != 0 && *recvbuf != NULL) {
+                kmem_free(*recvbuf, hdr->lmh_msglen);
+                *recvbuf = NULL;
+        }
         return (status);
 }
 
 /*
  * Hope this is interpreted per-zone...

@@ -591,34 +686,68 @@
  * Also limit the time smb_authsock_sendrecv() will wait
  * trying to send a request to the authentication service.
  */
 struct timeval smb_auth_send_tmo = { 15, 0 };
 
+/*
+ * Maximum time a user object may stay in state LOGGING_ON
+ */
+int smb_auth_total_tmo = 45;    /* seconds */
+
 static uint32_t
-smb_authsock_open(smb_user_t *user)
+smb_authsock_open(smb_request_t *sr)
 {
-        smb_server_t *sv = user->u_server;
+        smb_user_t *user = sr->uid_user;
+        smb_server_t *sv = sr->sr_server;
         ksocket_t so = NULL;
-        uint32_t status;
+        uint32_t status = 0;
         int rc;
 
         /*
-         * If the auth. service is busy, wait our turn.
-         * This may be frequent, so don't log.
+         * If the auth. service is busy, wait our turn.  This threshold
+         * limits the number of auth sockets we might have trying to
+         * communicate with the auth. service up in smbd.  Until we've
+         * set u_authsock, we need to "exit this threshold" in any
+         * error code paths after this "enter".
+         *
+         * Failure to "enter" may be frequent, so don't log.
          */
         if ((rc = smb_threshold_enter(&sv->sv_ssetup_ct)) != 0)
                 return (NT_STATUS_NO_LOGON_SERVERS);
 
         rc = ksocket_socket(&so, AF_UNIX, SOCK_STREAM, 0,
             KSOCKET_SLEEP, CRED());
         if (rc != 0) {
                 cmn_err(CE_NOTE, "smb_authsock_open: socket, rc=%d", rc);
+                smb_threshold_exit(&sv->sv_ssetup_ct);
                 status = NT_STATUS_INSUFF_SERVER_RESOURCES;
                 goto errout;
         }
 
         /*
+         * This (new) user object now gets an authsocket.
+         * Note: u_authsock cleanup in smb_user_logoff.
+         * After we've set u_authsock, smb_threshold_exit
+         * is done in smb_authsock_close().  If we somehow
+         * already have an authsock, close the new one and
+         * error out.
+         */
+        mutex_enter(&user->u_mutex);
+        if (user->u_authsock != NULL) {
+                mutex_exit(&user->u_mutex);
+                smb_authsock_close(user, so);
+                status = NT_STATUS_INTERNAL_ERROR;
+                goto errout;
+        }
+        user->u_authsock = so;
+        if (smb_auth_total_tmo != 0) {
+                user->u_auth_tmo = timeout(smb_user_auth_tmo, user,
+                    SEC_TO_TICK(smb_auth_total_tmo));
+        }
+        mutex_exit(&user->u_mutex);
+
+        /*
          * Set the send/recv timeouts.
          */
         (void) ksocket_setsockopt(so, SOL_SOCKET, SO_SNDTIMEO,
             &smb_auth_send_tmo, sizeof (smb_auth_send_tmo), CRED());
         (void) ksocket_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO,

@@ -628,34 +757,46 @@
          * Connect to the smbd auth. service.
          *
          * Would like to set the connect timeout too, but there's
          * apparently no easy way to do that for AF_UNIX.
          */
+        mutex_enter(&sr->sr_mutex);
+        if (sr->sr_state != SMB_REQ_STATE_ACTIVE) {
+                mutex_exit(&sr->sr_mutex);
+                status = NT_STATUS_CANCELLED;
+                goto errout;
+        }
+        sr->sr_state = SMB_REQ_STATE_WAITING_AUTH;
+        sr->cancel_method = smb_authsock_cancel;
+        sr->cancel_arg2 = user;
+        mutex_exit(&sr->sr_mutex);
+
         rc = ksocket_connect(so, (struct sockaddr *)&smbauth_sockname,
             sizeof (smbauth_sockname), CRED());
         if (rc != 0) {
                 DTRACE_PROBE1(error, int, rc);
                 status = NT_STATUS_NETLOGON_NOT_STARTED;
-                goto errout;
         }
 
-        /* Note: u_authsock cleanup in smb_authsock_close() */
-        mutex_enter(&user->u_mutex);
-        if (user->u_authsock != NULL) {
-                mutex_exit(&user->u_mutex);
+        mutex_enter(&sr->sr_mutex);
+        sr->cancel_method = NULL;
+        sr->cancel_arg2 = NULL;
+        switch (sr->sr_state) {
+        case SMB_REQ_STATE_WAITING_AUTH:
+                sr->sr_state = SMB_REQ_STATE_ACTIVE;
+                break;
+        case SMB_REQ_STATE_CANCEL_PENDING:
+                sr->sr_state = SMB_REQ_STATE_CANCELLED;
+                status = NT_STATUS_CANCELLED;
+                break;
+        default:
                 status = NT_STATUS_INTERNAL_ERROR;
-                goto errout;
+                break;
         }
-        user->u_authsock = so;
-        mutex_exit(&user->u_mutex);
-        return (0);
+        mutex_exit(&sr->sr_mutex);
 
 errout:
-        if (so != NULL)
-                (void) ksocket_close(so, CRED());
-        smb_threshold_exit(&sv->sv_ssetup_ct);
-
         return (status);
 }
 
 static int
 smb_authsock_send(ksocket_t so, void *buf, size_t len)

@@ -697,16 +838,18 @@
         }
 
         return (rc);
 }
 
+/*
+ * Caller has cleared user->u_authsock, passing the last ref
+ * as the 2nd arg here.  This can block, so it's called
+ * after exiting u_mutex.
+ */
 void
-smb_authsock_close(smb_user_t *user)
+smb_authsock_close(smb_user_t *user, ksocket_t so)
 {
 
-        ASSERT(MUTEX_HELD(&user->u_mutex));
-        if (user->u_authsock == NULL)
-                return;
-        (void) ksocket_close(user->u_authsock, CRED());
-        user->u_authsock = NULL;
+        (void) ksocket_shutdown(so, SHUT_RDWR, CRED());
+        (void) ksocket_close(so, CRED());
         smb_threshold_exit(&user->u_server->sv_ssetup_ct);
 }