1c398230bSWarner Losh /*- 2681a5bbeSBoris Popov * Copyright (c) 2000-2001 Boris Popov 3681a5bbeSBoris Popov * All rights reserved. 4681a5bbeSBoris Popov * 5681a5bbeSBoris Popov * Redistribution and use in source and binary forms, with or without 6681a5bbeSBoris Popov * modification, are permitted provided that the following conditions 7681a5bbeSBoris Popov * are met: 8681a5bbeSBoris Popov * 1. Redistributions of source code must retain the above copyright 9681a5bbeSBoris Popov * notice, this list of conditions and the following disclaimer. 10681a5bbeSBoris Popov * 2. Redistributions in binary form must reproduce the above copyright 11681a5bbeSBoris Popov * notice, this list of conditions and the following disclaimer in the 12681a5bbeSBoris Popov * documentation and/or other materials provided with the distribution. 13681a5bbeSBoris Popov * 14681a5bbeSBoris Popov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15681a5bbeSBoris Popov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16681a5bbeSBoris Popov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17681a5bbeSBoris Popov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18681a5bbeSBoris Popov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19681a5bbeSBoris Popov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20681a5bbeSBoris Popov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21681a5bbeSBoris Popov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22681a5bbeSBoris Popov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23681a5bbeSBoris Popov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24681a5bbeSBoris Popov * SUCH DAMAGE. 25681a5bbeSBoris Popov */ 26ab0de15bSDavid E. O'Brien 27681a5bbeSBoris Popov /* 28681a5bbeSBoris Popov * various SMB requests. Most of the routines merely packs data into mbufs. 29681a5bbeSBoris Popov */ 30ab0de15bSDavid E. O'Brien 31ab0de15bSDavid E. O'Brien #include <sys/cdefs.h> 32ab0de15bSDavid E. O'Brien __FBSDID("$FreeBSD$"); 33ab0de15bSDavid E. O'Brien 34681a5bbeSBoris Popov #include <sys/param.h> 35681a5bbeSBoris Popov #include <sys/systm.h> 36681a5bbeSBoris Popov #include <sys/kernel.h> 37681a5bbeSBoris Popov #include <sys/malloc.h> 38681a5bbeSBoris Popov #include <sys/proc.h> 39681a5bbeSBoris Popov #include <sys/lock.h> 40681a5bbeSBoris Popov #include <sys/sysctl.h> 41681a5bbeSBoris Popov #include <sys/socket.h> 42681a5bbeSBoris Popov #include <sys/uio.h> 43681a5bbeSBoris Popov 44681a5bbeSBoris Popov #include <sys/iconv.h> 45681a5bbeSBoris Popov 46681a5bbeSBoris Popov #include <netsmb/smb.h> 47681a5bbeSBoris Popov #include <netsmb/smb_subr.h> 48681a5bbeSBoris Popov #include <netsmb/smb_rq.h> 49681a5bbeSBoris Popov #include <netsmb/smb_conn.h> 50681a5bbeSBoris Popov #include <netsmb/smb_tran.h> 51681a5bbeSBoris Popov 52190b2c4fSTim J. Robbins #include "opt_netsmb.h" 53190b2c4fSTim J. Robbins 54681a5bbeSBoris Popov struct smb_dialect { 55681a5bbeSBoris Popov int d_id; 56681a5bbeSBoris Popov const char * d_name; 57681a5bbeSBoris Popov }; 58681a5bbeSBoris Popov 59681a5bbeSBoris Popov static struct smb_dialect smb_dialects[] = { 60681a5bbeSBoris Popov {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"}, 61681a5bbeSBoris Popov {SMB_DIALECT_COREPLUS, "MICROSOFT NETWORKS 1.03"}, 62681a5bbeSBoris Popov {SMB_DIALECT_LANMAN1_0, "MICROSOFT NETWORKS 3.0"}, 63681a5bbeSBoris Popov {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"}, 64681a5bbeSBoris Popov {SMB_DIALECT_LANMAN2_0, "LM1.2X002"}, 65681a5bbeSBoris Popov {SMB_DIALECT_LANMAN2_0, "Samba"}, 66681a5bbeSBoris Popov {SMB_DIALECT_NTLM0_12, "NT LANMAN 1.0"}, 67681a5bbeSBoris Popov {SMB_DIALECT_NTLM0_12, "NT LM 0.12"}, 68681a5bbeSBoris Popov {-1, NULL} 69681a5bbeSBoris Popov }; 70681a5bbeSBoris Popov 71681a5bbeSBoris Popov #define SMB_DIALECT_MAX (sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2) 72681a5bbeSBoris Popov 73e2a70cffSBoris Popov static u_int32_t 74e2a70cffSBoris Popov smb_vc_maxread(struct smb_vc *vcp) 75e2a70cffSBoris Popov { 76e2a70cffSBoris Popov /* 77e2a70cffSBoris Popov * Specs say up to 64k data bytes, but Windows traffic 78e2a70cffSBoris Popov * uses 60k... no doubt for some good reason. 79190b2c4fSTim J. Robbins * 80190b2c4fSTim J. Robbins * Don't exceed the server's buffer size if signatures 81190b2c4fSTim J. Robbins * are enabled otherwise Windows 2003 chokes. Allow space 82190b2c4fSTim J. Robbins * for the SMB header & a little bit extra. 83e2a70cffSBoris Popov */ 84190b2c4fSTim J. Robbins if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) && 85190b2c4fSTim J. Robbins (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0) 86e2a70cffSBoris Popov return (60*1024); 87e2a70cffSBoris Popov else 88190b2c4fSTim J. Robbins return (vcp->vc_sopt.sv_maxtx - SMB_HDRLEN - 64); 89e2a70cffSBoris Popov } 90e2a70cffSBoris Popov 91e2a70cffSBoris Popov static u_int32_t 92e2a70cffSBoris Popov smb_vc_maxwrite(struct smb_vc *vcp) 93e2a70cffSBoris Popov { 94e2a70cffSBoris Popov /* 95190b2c4fSTim J. Robbins * See comment above. 96e2a70cffSBoris Popov */ 97190b2c4fSTim J. Robbins if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) && 98190b2c4fSTim J. Robbins (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0) 99e2a70cffSBoris Popov return (60*1024); 100e2a70cffSBoris Popov else 101190b2c4fSTim J. Robbins return (vcp->vc_sopt.sv_maxtx - SMB_HDRLEN - 64); 102e2a70cffSBoris Popov } 103e2a70cffSBoris Popov 104681a5bbeSBoris Popov static int 105681a5bbeSBoris Popov smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name) 106681a5bbeSBoris Popov { 107fce6fbfaSBoris Popov if (scred->scr_td->td_proc == vcp->vc_iod->iod_p) 108681a5bbeSBoris Popov return 0; 109681a5bbeSBoris Popov SMBERROR("wrong function called(%s)\n", name); 110681a5bbeSBoris Popov return EINVAL; 111681a5bbeSBoris Popov } 112681a5bbeSBoris Popov 113681a5bbeSBoris Popov int 114681a5bbeSBoris Popov smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) 115681a5bbeSBoris Popov { 116681a5bbeSBoris Popov struct smb_dialect *dp; 117681a5bbeSBoris Popov struct smb_sopt *sp = NULL; 118681a5bbeSBoris Popov struct smb_rq *rqp; 119681a5bbeSBoris Popov struct mbchain *mbp; 120681a5bbeSBoris Popov struct mdchain *mdp; 121681a5bbeSBoris Popov u_int8_t wc, stime[8], sblen; 122681a5bbeSBoris Popov u_int16_t dindex, tw, tw1, swlen, bc; 123681a5bbeSBoris Popov int error, maxqsz; 124*41f1dcccSKevin Lo int unicode = SMB_UNICODE_STRINGS(vcp); 125*41f1dcccSKevin Lo void * servercharset = vcp->vc_toserver; 126*41f1dcccSKevin Lo void * localcharset = vcp->vc_tolocal; 127681a5bbeSBoris Popov 1286e551fb6SDavid E. O'Brien if (smb_smb_nomux(vcp, scred, __func__) != 0) 129681a5bbeSBoris Popov return EINVAL; 130*41f1dcccSKevin Lo /* Disable Unicode for SMB_COM_NEGOTIATE requests */ 131*41f1dcccSKevin Lo if (unicode) { 132*41f1dcccSKevin Lo vcp->vc_toserver = vcp->vc_cp_toserver; 133*41f1dcccSKevin Lo vcp->vc_tolocal = vcp->vc_cp_tolocal; 134*41f1dcccSKevin Lo } 135681a5bbeSBoris Popov vcp->vc_hflags = 0; 136681a5bbeSBoris Popov vcp->vc_hflags2 = 0; 137681a5bbeSBoris Popov vcp->obj.co_flags &= ~(SMBV_ENCRYPT); 138681a5bbeSBoris Popov sp = &vcp->vc_sopt; 139681a5bbeSBoris Popov bzero(sp, sizeof(struct smb_sopt)); 140681a5bbeSBoris Popov error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp); 141681a5bbeSBoris Popov if (error) 142681a5bbeSBoris Popov return error; 143681a5bbeSBoris Popov smb_rq_getrequest(rqp, &mbp); 144681a5bbeSBoris Popov smb_rq_wstart(rqp); 145681a5bbeSBoris Popov smb_rq_wend(rqp); 146681a5bbeSBoris Popov smb_rq_bstart(rqp); 147681a5bbeSBoris Popov for(dp = smb_dialects; dp->d_id != -1; dp++) { 148681a5bbeSBoris Popov mb_put_uint8(mbp, SMB_DT_DIALECT); 149681a5bbeSBoris Popov smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE); 150681a5bbeSBoris Popov } 151681a5bbeSBoris Popov smb_rq_bend(rqp); 152681a5bbeSBoris Popov error = smb_rq_simple(rqp); 153681a5bbeSBoris Popov SMBSDEBUG("%d\n", error); 154681a5bbeSBoris Popov if (error) 155681a5bbeSBoris Popov goto bad; 156681a5bbeSBoris Popov smb_rq_getreply(rqp, &mdp); 157681a5bbeSBoris Popov do { 158681a5bbeSBoris Popov error = md_get_uint8(mdp, &wc); 159681a5bbeSBoris Popov if (error) 160681a5bbeSBoris Popov break; 161681a5bbeSBoris Popov error = md_get_uint16le(mdp, &dindex); 162681a5bbeSBoris Popov if (error) 163681a5bbeSBoris Popov break; 164681a5bbeSBoris Popov if (dindex > 7) { 165681a5bbeSBoris Popov SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex); 166681a5bbeSBoris Popov error = EBADRPC; 167681a5bbeSBoris Popov break; 168681a5bbeSBoris Popov } 169681a5bbeSBoris Popov dp = smb_dialects + dindex; 170681a5bbeSBoris Popov sp->sv_proto = dp->d_id; 171681a5bbeSBoris Popov SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc); 172681a5bbeSBoris Popov error = EBADRPC; 173681a5bbeSBoris Popov if (dp->d_id >= SMB_DIALECT_NTLM0_12) { 174681a5bbeSBoris Popov if (wc != 17) 175681a5bbeSBoris Popov break; 176681a5bbeSBoris Popov md_get_uint8(mdp, &sp->sv_sm); 177681a5bbeSBoris Popov md_get_uint16le(mdp, &sp->sv_maxmux); 178681a5bbeSBoris Popov md_get_uint16le(mdp, &sp->sv_maxvcs); 179681a5bbeSBoris Popov md_get_uint32le(mdp, &sp->sv_maxtx); 180681a5bbeSBoris Popov md_get_uint32le(mdp, &sp->sv_maxraw); 181681a5bbeSBoris Popov md_get_uint32le(mdp, &sp->sv_skey); 182681a5bbeSBoris Popov md_get_uint32le(mdp, &sp->sv_caps); 183681a5bbeSBoris Popov md_get_mem(mdp, stime, 8, MB_MSYSTEM); 184681a5bbeSBoris Popov md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); 185681a5bbeSBoris Popov md_get_uint8(mdp, &sblen); 186681a5bbeSBoris Popov if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) { 187681a5bbeSBoris Popov if (sblen != SMB_MAXCHALLENGELEN) { 188681a5bbeSBoris Popov SMBERROR("Unexpected length of security blob (%d)\n", sblen); 189681a5bbeSBoris Popov break; 190681a5bbeSBoris Popov } 191*41f1dcccSKevin Lo error = md_get_uint16le(mdp, &bc); 192681a5bbeSBoris Popov if (error) 193681a5bbeSBoris Popov break; 194681a5bbeSBoris Popov if (sp->sv_caps & SMB_CAP_EXT_SECURITY) 195681a5bbeSBoris Popov md_get_mem(mdp, NULL, 16, MB_MSYSTEM); 196681a5bbeSBoris Popov error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM); 197681a5bbeSBoris Popov if (error) 198681a5bbeSBoris Popov break; 199681a5bbeSBoris Popov vcp->vc_chlen = sblen; 200681a5bbeSBoris Popov vcp->obj.co_flags |= SMBV_ENCRYPT; 201681a5bbeSBoris Popov } 202190b2c4fSTim J. Robbins if (sp->sv_sm & SMB_SM_SIGS_REQUIRE) 203190b2c4fSTim J. Robbins vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE; 204*41f1dcccSKevin Lo if (vcp->vc_ucs_toserver && 205*41f1dcccSKevin Lo sp->sv_caps & SMB_CAP_UNICODE) { 206*41f1dcccSKevin Lo /* 207*41f1dcccSKevin Lo * They do Unicode. 208*41f1dcccSKevin Lo */ 209*41f1dcccSKevin Lo vcp->obj.co_flags |= SMBV_UNICODE; 210*41f1dcccSKevin Lo } 211681a5bbeSBoris Popov vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; 212681a5bbeSBoris Popov if (dp->d_id == SMB_DIALECT_NTLM0_12 && 213681a5bbeSBoris Popov sp->sv_maxtx < 4096 && 214681a5bbeSBoris Popov (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) { 215681a5bbeSBoris Popov vcp->obj.co_flags |= SMBV_WIN95; 216681a5bbeSBoris Popov SMBSDEBUG("Win95 detected\n"); 217681a5bbeSBoris Popov } 218*41f1dcccSKevin Lo error = 0; 219*41f1dcccSKevin Lo break; 220*41f1dcccSKevin Lo } 221*41f1dcccSKevin Lo vcp->vc_hflags2 &= ~(SMB_FLAGS2_EXT_SEC|SMB_FLAGS2_DFS| 222*41f1dcccSKevin Lo SMB_FLAGS2_ERR_STATUS|SMB_FLAGS2_UNICODE); 223*41f1dcccSKevin Lo unicode = 0; 224*41f1dcccSKevin Lo if (dp->d_id > SMB_DIALECT_CORE) { 225681a5bbeSBoris Popov md_get_uint16le(mdp, &tw); 226681a5bbeSBoris Popov sp->sv_sm = tw; 227681a5bbeSBoris Popov md_get_uint16le(mdp, &tw); 228681a5bbeSBoris Popov sp->sv_maxtx = tw; 229681a5bbeSBoris Popov md_get_uint16le(mdp, &sp->sv_maxmux); 230681a5bbeSBoris Popov md_get_uint16le(mdp, &sp->sv_maxvcs); 231681a5bbeSBoris Popov md_get_uint16le(mdp, &tw); /* rawmode */ 232681a5bbeSBoris Popov md_get_uint32le(mdp, &sp->sv_skey); 233681a5bbeSBoris Popov if (wc == 13) { /* >= LANMAN1 */ 234681a5bbeSBoris Popov md_get_uint16(mdp, &tw); /* time */ 235681a5bbeSBoris Popov md_get_uint16(mdp, &tw1); /* date */ 236681a5bbeSBoris Popov md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); 237681a5bbeSBoris Popov md_get_uint16le(mdp, &swlen); 238681a5bbeSBoris Popov if (swlen > SMB_MAXCHALLENGELEN) 239681a5bbeSBoris Popov break; 240681a5bbeSBoris Popov md_get_uint16(mdp, NULL); /* mbz */ 241*41f1dcccSKevin Lo if (md_get_uint16le(mdp, &bc) != 0) 242681a5bbeSBoris Popov break; 243681a5bbeSBoris Popov if (bc < swlen) 244681a5bbeSBoris Popov break; 245681a5bbeSBoris Popov if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) { 246681a5bbeSBoris Popov error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM); 247681a5bbeSBoris Popov if (error) 248681a5bbeSBoris Popov break; 249681a5bbeSBoris Popov vcp->vc_chlen = swlen; 250681a5bbeSBoris Popov vcp->obj.co_flags |= SMBV_ENCRYPT; 251681a5bbeSBoris Popov } 252681a5bbeSBoris Popov } 253681a5bbeSBoris Popov vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; 254681a5bbeSBoris Popov } else { /* an old CORE protocol */ 255681a5bbeSBoris Popov sp->sv_maxmux = 1; 256681a5bbeSBoris Popov } 257681a5bbeSBoris Popov error = 0; 258681a5bbeSBoris Popov } while (0); 259681a5bbeSBoris Popov if (error == 0) { 260681a5bbeSBoris Popov vcp->vc_maxvcs = sp->sv_maxvcs; 261681a5bbeSBoris Popov if (vcp->vc_maxvcs <= 1) { 262681a5bbeSBoris Popov if (vcp->vc_maxvcs == 0) 263681a5bbeSBoris Popov vcp->vc_maxvcs = 1; 264681a5bbeSBoris Popov } 265681a5bbeSBoris Popov if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff) 266681a5bbeSBoris Popov sp->sv_maxtx = 1024; 267e2a70cffSBoris Popov else 268e2a70cffSBoris Popov sp->sv_maxtx = min(sp->sv_maxtx, 269e2a70cffSBoris Popov 63*1024 + SMB_HDRLEN + 16); 270e2a70cffSBoris Popov SMB_TRAN_GETPARAM(vcp, SMBTP_RCVSZ, &maxqsz); 271e2a70cffSBoris Popov vcp->vc_rxmax = min(smb_vc_maxread(vcp), maxqsz - 1024); 272681a5bbeSBoris Popov SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz); 273e2a70cffSBoris Popov vcp->vc_wxmax = min(smb_vc_maxwrite(vcp), maxqsz - 1024); 274681a5bbeSBoris Popov vcp->vc_txmax = min(sp->sv_maxtx, maxqsz); 275681a5bbeSBoris Popov SMBSDEBUG("TZ = %d\n", sp->sv_tz); 276681a5bbeSBoris Popov SMBSDEBUG("CAPS = %x\n", sp->sv_caps); 277681a5bbeSBoris Popov SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux); 278681a5bbeSBoris Popov SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs); 279681a5bbeSBoris Popov SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw); 280681a5bbeSBoris Popov SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx); 281681a5bbeSBoris Popov } 282681a5bbeSBoris Popov bad: 283*41f1dcccSKevin Lo /* Restore Unicode conversion state */ 284*41f1dcccSKevin Lo if (unicode) { 285*41f1dcccSKevin Lo vcp->vc_toserver = servercharset; 286*41f1dcccSKevin Lo vcp->vc_tolocal = localcharset; 287*41f1dcccSKevin Lo vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE; 288*41f1dcccSKevin Lo } 289681a5bbeSBoris Popov smb_rq_done(rqp); 290681a5bbeSBoris Popov return error; 291681a5bbeSBoris Popov } 292681a5bbeSBoris Popov 293681a5bbeSBoris Popov int 294681a5bbeSBoris Popov smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) 295681a5bbeSBoris Popov { 296681a5bbeSBoris Popov struct smb_rq *rqp; 297681a5bbeSBoris Popov struct mbchain *mbp; 298681a5bbeSBoris Popov /* u_int8_t wc; 299681a5bbeSBoris Popov u_int16_t tw, tw1;*/ 300681a5bbeSBoris Popov smb_uniptr unipp, ntencpass = NULL; 301681a5bbeSBoris Popov char *pp, *up, *pbuf, *encpass; 30270f9b9d9SBoris Popov int error, plen, uniplen, ulen, upper; 303*41f1dcccSKevin Lo u_int32_t caps = 0; 30470f9b9d9SBoris Popov 30570f9b9d9SBoris Popov upper = 0; 30670f9b9d9SBoris Popov 307*41f1dcccSKevin Lo if (vcp->obj.co_flags & SMBV_UNICODE) 308*41f1dcccSKevin Lo caps |= SMB_CAP_UNICODE; 309*41f1dcccSKevin Lo 31070f9b9d9SBoris Popov again: 311681a5bbeSBoris Popov 312681a5bbeSBoris Popov vcp->vc_smbuid = SMB_UID_UNKNOWN; 313681a5bbeSBoris Popov 3146e551fb6SDavid E. O'Brien if (smb_smb_nomux(vcp, scred, __func__) != 0) 315681a5bbeSBoris Popov return EINVAL; 316681a5bbeSBoris Popov 317681a5bbeSBoris Popov error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp); 318681a5bbeSBoris Popov if (error) 319681a5bbeSBoris Popov return error; 320a163d034SWarner Losh pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); 321a163d034SWarner Losh encpass = malloc(24, M_SMBTEMP, M_WAITOK); 322681a5bbeSBoris Popov if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { 32370f9b9d9SBoris Popov /* 32470f9b9d9SBoris Popov * We try w/o uppercasing first so Samba mixed case 32570f9b9d9SBoris Popov * passwords work. If that fails we come back and try 32670f9b9d9SBoris Popov * uppercasing to satisfy OS/2 and Windows for Workgroups. 32770f9b9d9SBoris Popov */ 32870f9b9d9SBoris Popov if (upper++) { 32970f9b9d9SBoris Popov iconv_convstr(vcp->vc_toupper, pbuf, 33070f9b9d9SBoris Popov smb_vc_getpass(vcp)/*, SMB_MAXPASSWORDLEN*/); 33170f9b9d9SBoris Popov } else { 33270f9b9d9SBoris Popov strncpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN); 33370f9b9d9SBoris Popov pbuf[SMB_MAXPASSWORDLEN] = '\0'; 33470f9b9d9SBoris Popov } 33570f9b9d9SBoris Popov if (!SMB_UNICODE_STRINGS(vcp)) 33670f9b9d9SBoris Popov iconv_convstr(vcp->vc_toserver, pbuf, pbuf/*, 33770f9b9d9SBoris Popov SMB_MAXPASSWORDLEN*/); 33870f9b9d9SBoris Popov 339681a5bbeSBoris Popov if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { 340681a5bbeSBoris Popov uniplen = plen = 24; 341681a5bbeSBoris Popov smb_encrypt(pbuf, vcp->vc_ch, encpass); 342a163d034SWarner Losh ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK); 34370f9b9d9SBoris Popov if (SMB_UNICODE_STRINGS(vcp)) { 34470f9b9d9SBoris Popov strncpy(pbuf, smb_vc_getpass(vcp), 34570f9b9d9SBoris Popov SMB_MAXPASSWORDLEN); 34670f9b9d9SBoris Popov pbuf[SMB_MAXPASSWORDLEN] = '\0'; 34770f9b9d9SBoris Popov } else 34870f9b9d9SBoris Popov iconv_convstr(vcp->vc_toserver, pbuf, 34970f9b9d9SBoris Popov smb_vc_getpass(vcp)/*, 35070f9b9d9SBoris Popov SMB_MAXPASSWORDLEN*/); 351681a5bbeSBoris Popov smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass); 352681a5bbeSBoris Popov pp = encpass; 353681a5bbeSBoris Popov unipp = ntencpass; 354681a5bbeSBoris Popov } else { 355681a5bbeSBoris Popov plen = strlen(pbuf) + 1; 356681a5bbeSBoris Popov pp = pbuf; 357681a5bbeSBoris Popov uniplen = plen * 2; 358a163d034SWarner Losh ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK); 359681a5bbeSBoris Popov smb_strtouni(ntencpass, smb_vc_getpass(vcp)); 360681a5bbeSBoris Popov plen--; 361daa35dedSBoris Popov 362daa35dedSBoris Popov /* 363daa35dedSBoris Popov * The uniplen is zeroed because Samba cannot deal 364daa35dedSBoris Popov * with this 2nd cleartext password. This Samba 365daa35dedSBoris Popov * "bug" is actually a workaround for problems in 366daa35dedSBoris Popov * Microsoft clients. 367daa35dedSBoris Popov */ 368681a5bbeSBoris Popov uniplen = 0/*-= 2*/; 369681a5bbeSBoris Popov unipp = ntencpass; 370681a5bbeSBoris Popov } 371681a5bbeSBoris Popov } else { 372681a5bbeSBoris Popov /* 373681a5bbeSBoris Popov * In the share security mode password will be used 374681a5bbeSBoris Popov * only in the tree authentication 375681a5bbeSBoris Popov */ 376681a5bbeSBoris Popov pp = ""; 377681a5bbeSBoris Popov plen = 1; 378681a5bbeSBoris Popov unipp = &smb_unieol; 379cf37bfb0SMax Khon uniplen = 0 /* sizeof(smb_unieol) */; 380681a5bbeSBoris Popov } 381681a5bbeSBoris Popov smb_rq_wstart(rqp); 382681a5bbeSBoris Popov mbp = &rqp->sr_rq; 383681a5bbeSBoris Popov up = vcp->vc_username; 384681a5bbeSBoris Popov ulen = strlen(up) + 1; 38570f9b9d9SBoris Popov /* 38670f9b9d9SBoris Popov * If userid is null we are attempting anonymous browse login 38770f9b9d9SBoris Popov * so passwords must be zero length. 38870f9b9d9SBoris Popov */ 38970f9b9d9SBoris Popov if (ulen == 1) 39070f9b9d9SBoris Popov plen = uniplen = 0; 391681a5bbeSBoris Popov mb_put_uint8(mbp, 0xff); 392681a5bbeSBoris Popov mb_put_uint8(mbp, 0); 393681a5bbeSBoris Popov mb_put_uint16le(mbp, 0); 394681a5bbeSBoris Popov mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx); 395681a5bbeSBoris Popov mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux); 396681a5bbeSBoris Popov mb_put_uint16le(mbp, vcp->vc_number); 397681a5bbeSBoris Popov mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey); 398681a5bbeSBoris Popov mb_put_uint16le(mbp, plen); 399681a5bbeSBoris Popov if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) { 400681a5bbeSBoris Popov mb_put_uint32le(mbp, 0); 401681a5bbeSBoris Popov smb_rq_wend(rqp); 402681a5bbeSBoris Popov smb_rq_bstart(rqp); 403681a5bbeSBoris Popov mb_put_mem(mbp, pp, plen, MB_MSYSTEM); 404681a5bbeSBoris Popov smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); 405681a5bbeSBoris Popov } else { 406681a5bbeSBoris Popov mb_put_uint16le(mbp, uniplen); 407681a5bbeSBoris Popov mb_put_uint32le(mbp, 0); /* reserved */ 408*41f1dcccSKevin Lo mb_put_uint32le(mbp, caps); 409681a5bbeSBoris Popov smb_rq_wend(rqp); 410681a5bbeSBoris Popov smb_rq_bstart(rqp); 411681a5bbeSBoris Popov mb_put_mem(mbp, pp, plen, MB_MSYSTEM); 412681a5bbeSBoris Popov mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM); 413681a5bbeSBoris Popov smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* AccountName */ 414681a5bbeSBoris Popov smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */ 415681a5bbeSBoris Popov smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE); /* Client's OS */ 416681a5bbeSBoris Popov smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE); /* Client name */ 417681a5bbeSBoris Popov } 418681a5bbeSBoris Popov smb_rq_bend(rqp); 419681a5bbeSBoris Popov if (ntencpass) 420681a5bbeSBoris Popov free(ntencpass, M_SMBTEMP); 421190b2c4fSTim J. Robbins if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) 422190b2c4fSTim J. Robbins smb_calcmackey(vcp); 423681a5bbeSBoris Popov error = smb_rq_simple(rqp); 424681a5bbeSBoris Popov SMBSDEBUG("%d\n", error); 425681a5bbeSBoris Popov if (error) { 426681a5bbeSBoris Popov if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess) 427681a5bbeSBoris Popov error = EAUTH; 428681a5bbeSBoris Popov goto bad; 429681a5bbeSBoris Popov } 430681a5bbeSBoris Popov vcp->vc_smbuid = rqp->sr_rpuid; 431681a5bbeSBoris Popov bad: 432681a5bbeSBoris Popov free(encpass, M_SMBTEMP); 433681a5bbeSBoris Popov free(pbuf, M_SMBTEMP); 434681a5bbeSBoris Popov smb_rq_done(rqp); 43570f9b9d9SBoris Popov if (error && upper == 1 && vcp->vc_sopt.sv_sm & SMB_SM_USER) 43670f9b9d9SBoris Popov goto again; 437681a5bbeSBoris Popov return error; 438681a5bbeSBoris Popov } 439681a5bbeSBoris Popov 440681a5bbeSBoris Popov int 441681a5bbeSBoris Popov smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred) 442681a5bbeSBoris Popov { 443681a5bbeSBoris Popov struct smb_rq *rqp; 444681a5bbeSBoris Popov struct mbchain *mbp; 445681a5bbeSBoris Popov int error; 446681a5bbeSBoris Popov 447681a5bbeSBoris Popov if (vcp->vc_smbuid == SMB_UID_UNKNOWN) 448681a5bbeSBoris Popov return 0; 449681a5bbeSBoris Popov 4506e551fb6SDavid E. O'Brien if (smb_smb_nomux(vcp, scred, __func__) != 0) 451681a5bbeSBoris Popov return EINVAL; 452681a5bbeSBoris Popov 453681a5bbeSBoris Popov error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp); 454681a5bbeSBoris Popov if (error) 455681a5bbeSBoris Popov return error; 456681a5bbeSBoris Popov mbp = &rqp->sr_rq; 457681a5bbeSBoris Popov smb_rq_wstart(rqp); 458681a5bbeSBoris Popov mb_put_uint8(mbp, 0xff); 459681a5bbeSBoris Popov mb_put_uint8(mbp, 0); 460681a5bbeSBoris Popov mb_put_uint16le(mbp, 0); 461681a5bbeSBoris Popov smb_rq_wend(rqp); 462681a5bbeSBoris Popov smb_rq_bstart(rqp); 463681a5bbeSBoris Popov smb_rq_bend(rqp); 464681a5bbeSBoris Popov error = smb_rq_simple(rqp); 465681a5bbeSBoris Popov SMBSDEBUG("%d\n", error); 466681a5bbeSBoris Popov smb_rq_done(rqp); 467681a5bbeSBoris Popov return error; 468681a5bbeSBoris Popov } 469681a5bbeSBoris Popov 470681a5bbeSBoris Popov static char smb_any_share[] = "?????"; 471681a5bbeSBoris Popov 472681a5bbeSBoris Popov static char * 473681a5bbeSBoris Popov smb_share_typename(int stype) 474681a5bbeSBoris Popov { 475681a5bbeSBoris Popov char *pp; 476681a5bbeSBoris Popov 477681a5bbeSBoris Popov switch (stype) { 478681a5bbeSBoris Popov case SMB_ST_DISK: 479681a5bbeSBoris Popov pp = "A:"; 480681a5bbeSBoris Popov break; 481681a5bbeSBoris Popov case SMB_ST_PRINTER: 482681a5bbeSBoris Popov pp = smb_any_share; /* can't use LPT: here... */ 483681a5bbeSBoris Popov break; 484681a5bbeSBoris Popov case SMB_ST_PIPE: 485681a5bbeSBoris Popov pp = "IPC"; 486681a5bbeSBoris Popov break; 487681a5bbeSBoris Popov case SMB_ST_COMM: 488681a5bbeSBoris Popov pp = "COMM"; 489681a5bbeSBoris Popov break; 490681a5bbeSBoris Popov case SMB_ST_ANY: 491681a5bbeSBoris Popov default: 492681a5bbeSBoris Popov pp = smb_any_share; 493681a5bbeSBoris Popov break; 494681a5bbeSBoris Popov } 495681a5bbeSBoris Popov return pp; 496681a5bbeSBoris Popov } 497681a5bbeSBoris Popov 498681a5bbeSBoris Popov int 499681a5bbeSBoris Popov smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) 500681a5bbeSBoris Popov { 501681a5bbeSBoris Popov struct smb_vc *vcp; 502681a5bbeSBoris Popov struct smb_rq rq, *rqp = &rq; 503681a5bbeSBoris Popov struct mbchain *mbp; 504681a5bbeSBoris Popov char *pp, *pbuf, *encpass; 50570f9b9d9SBoris Popov int error, plen, caseopt, upper; 50670f9b9d9SBoris Popov 50770f9b9d9SBoris Popov upper = 0; 50870f9b9d9SBoris Popov 50970f9b9d9SBoris Popov again: 51070f9b9d9SBoris Popov /* Disable Unicode for SMB_COM_TREE_CONNECT_ANDX requests */ 51170f9b9d9SBoris Popov if (SSTOVC(ssp)->vc_hflags2 & SMB_FLAGS2_UNICODE) { 51270f9b9d9SBoris Popov vcp = SSTOVC(ssp); 513*41f1dcccSKevin Lo vcp->vc_toserver = vcp->vc_cp_toserver; 514*41f1dcccSKevin Lo vcp->vc_tolocal = vcp->vc_cp_tolocal; 51570f9b9d9SBoris Popov vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE; 51670f9b9d9SBoris Popov } 517681a5bbeSBoris Popov 518681a5bbeSBoris Popov ssp->ss_tid = SMB_TID_UNKNOWN; 519681a5bbeSBoris Popov error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp); 520681a5bbeSBoris Popov if (error) 521681a5bbeSBoris Popov return error; 522681a5bbeSBoris Popov vcp = rqp->sr_vc; 523681a5bbeSBoris Popov caseopt = SMB_CS_NONE; 524681a5bbeSBoris Popov if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { 525681a5bbeSBoris Popov plen = 1; 526681a5bbeSBoris Popov pp = ""; 527681a5bbeSBoris Popov pbuf = NULL; 528681a5bbeSBoris Popov encpass = NULL; 529681a5bbeSBoris Popov } else { 530a163d034SWarner Losh pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); 531a163d034SWarner Losh encpass = malloc(24, M_SMBTEMP, M_WAITOK); 53270f9b9d9SBoris Popov /* 53370f9b9d9SBoris Popov * We try w/o uppercasing first so Samba mixed case 53470f9b9d9SBoris Popov * passwords work. If that fails we come back and try 53570f9b9d9SBoris Popov * uppercasing to satisfy OS/2 and Windows for Workgroups. 53670f9b9d9SBoris Popov */ 53770f9b9d9SBoris Popov if (upper++) { 53870f9b9d9SBoris Popov iconv_convstr(vcp->vc_toupper, pbuf, 53970f9b9d9SBoris Popov smb_share_getpass(ssp)/*, 54070f9b9d9SBoris Popov SMB_MAXPASSWORDLEN*/); 54170f9b9d9SBoris Popov } else { 54270f9b9d9SBoris Popov strncpy(pbuf, smb_share_getpass(ssp), 54370f9b9d9SBoris Popov SMB_MAXPASSWORDLEN); 54470f9b9d9SBoris Popov pbuf[SMB_MAXPASSWORDLEN] = '\0'; 54570f9b9d9SBoris Popov } 546681a5bbeSBoris Popov if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { 547681a5bbeSBoris Popov plen = 24; 548681a5bbeSBoris Popov smb_encrypt(pbuf, vcp->vc_ch, encpass); 549681a5bbeSBoris Popov pp = encpass; 550681a5bbeSBoris Popov } else { 551681a5bbeSBoris Popov plen = strlen(pbuf) + 1; 552681a5bbeSBoris Popov pp = pbuf; 553681a5bbeSBoris Popov } 554681a5bbeSBoris Popov } 555681a5bbeSBoris Popov mbp = &rqp->sr_rq; 556681a5bbeSBoris Popov smb_rq_wstart(rqp); 557681a5bbeSBoris Popov mb_put_uint8(mbp, 0xff); 558681a5bbeSBoris Popov mb_put_uint8(mbp, 0); 559681a5bbeSBoris Popov mb_put_uint16le(mbp, 0); 560681a5bbeSBoris Popov mb_put_uint16le(mbp, 0); /* Flags */ 561681a5bbeSBoris Popov mb_put_uint16le(mbp, plen); 562681a5bbeSBoris Popov smb_rq_wend(rqp); 563681a5bbeSBoris Popov smb_rq_bstart(rqp); 564681a5bbeSBoris Popov mb_put_mem(mbp, pp, plen, MB_MSYSTEM); 565681a5bbeSBoris Popov smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt); 566681a5bbeSBoris Popov pp = vcp->vc_srvname; 567681a5bbeSBoris Popov smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt); 568681a5bbeSBoris Popov smb_put_dmem(mbp, vcp, "\\", 1, caseopt); 569681a5bbeSBoris Popov pp = ssp->ss_name; 570681a5bbeSBoris Popov smb_put_dstring(mbp, vcp, pp, caseopt); 571681a5bbeSBoris Popov pp = smb_share_typename(ssp->ss_type); 572681a5bbeSBoris Popov smb_put_dstring(mbp, vcp, pp, caseopt); 573681a5bbeSBoris Popov smb_rq_bend(rqp); 574681a5bbeSBoris Popov error = smb_rq_simple(rqp); 575681a5bbeSBoris Popov SMBSDEBUG("%d\n", error); 576681a5bbeSBoris Popov if (error) 577681a5bbeSBoris Popov goto bad; 578681a5bbeSBoris Popov ssp->ss_tid = rqp->sr_rptid; 579681a5bbeSBoris Popov ssp->ss_vcgenid = vcp->vc_genid; 580681a5bbeSBoris Popov ssp->ss_flags |= SMBS_CONNECTED; 581*41f1dcccSKevin Lo /* 582*41f1dcccSKevin Lo * If the server can speak Unicode then switch 583*41f1dcccSKevin Lo * our converters to do Unicode <--> Local 584*41f1dcccSKevin Lo */ 585*41f1dcccSKevin Lo if (vcp->obj.co_flags & SMBV_UNICODE) { 586*41f1dcccSKevin Lo vcp->vc_toserver = vcp->vc_ucs_toserver; 587*41f1dcccSKevin Lo vcp->vc_tolocal = vcp->vc_ucs_tolocal; 588*41f1dcccSKevin Lo vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE; 589*41f1dcccSKevin Lo } 590681a5bbeSBoris Popov bad: 591681a5bbeSBoris Popov if (encpass) 592681a5bbeSBoris Popov free(encpass, M_SMBTEMP); 593681a5bbeSBoris Popov if (pbuf) 594681a5bbeSBoris Popov free(pbuf, M_SMBTEMP); 595681a5bbeSBoris Popov smb_rq_done(rqp); 59670f9b9d9SBoris Popov if (error && upper == 1) 59770f9b9d9SBoris Popov goto again; 598681a5bbeSBoris Popov return error; 599681a5bbeSBoris Popov } 600681a5bbeSBoris Popov 601681a5bbeSBoris Popov int 602681a5bbeSBoris Popov smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) 603681a5bbeSBoris Popov { 604681a5bbeSBoris Popov struct smb_rq *rqp; 605681a5bbeSBoris Popov struct mbchain *mbp; 606681a5bbeSBoris Popov int error; 607681a5bbeSBoris Popov 608681a5bbeSBoris Popov if (ssp->ss_tid == SMB_TID_UNKNOWN) 609681a5bbeSBoris Popov return 0; 610681a5bbeSBoris Popov error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp); 611681a5bbeSBoris Popov if (error) 612681a5bbeSBoris Popov return error; 613681a5bbeSBoris Popov mbp = &rqp->sr_rq; 614681a5bbeSBoris Popov smb_rq_wstart(rqp); 615681a5bbeSBoris Popov smb_rq_wend(rqp); 616681a5bbeSBoris Popov smb_rq_bstart(rqp); 617681a5bbeSBoris Popov smb_rq_bend(rqp); 618681a5bbeSBoris Popov error = smb_rq_simple(rqp); 619681a5bbeSBoris Popov SMBSDEBUG("%d\n", error); 620681a5bbeSBoris Popov smb_rq_done(rqp); 621681a5bbeSBoris Popov ssp->ss_tid = SMB_TID_UNKNOWN; 622681a5bbeSBoris Popov return error; 623681a5bbeSBoris Popov } 624681a5bbeSBoris Popov 625681a5bbeSBoris Popov static __inline int 626e2a70cffSBoris Popov smb_smb_readx(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, 627e2a70cffSBoris Popov struct uio *uio, struct smb_cred *scred) 628e2a70cffSBoris Popov { 629e2a70cffSBoris Popov struct smb_rq *rqp; 630e2a70cffSBoris Popov struct mbchain *mbp; 631e2a70cffSBoris Popov struct mdchain *mdp; 632e2a70cffSBoris Popov u_int8_t wc; 633e2a70cffSBoris Popov int error; 634e2a70cffSBoris Popov u_int16_t residhi, residlo, off, doff; 635e2a70cffSBoris Popov u_int32_t resid; 636e2a70cffSBoris Popov 637e2a70cffSBoris Popov error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp); 638e2a70cffSBoris Popov if (error) 639e2a70cffSBoris Popov return error; 640e2a70cffSBoris Popov smb_rq_getrequest(rqp, &mbp); 641e2a70cffSBoris Popov smb_rq_wstart(rqp); 642e2a70cffSBoris Popov mb_put_uint8(mbp, 0xff); /* no secondary command */ 643e2a70cffSBoris Popov mb_put_uint8(mbp, 0); /* MBZ */ 644e2a70cffSBoris Popov mb_put_uint16le(mbp, 0); /* offset to secondary */ 645e2a70cffSBoris Popov mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); 646e2a70cffSBoris Popov mb_put_uint32le(mbp, uio->uio_offset); 647e2a70cffSBoris Popov *len = min(SSTOVC(ssp)->vc_rxmax, *len); 648e2a70cffSBoris Popov mb_put_uint16le(mbp, *len); /* MaxCount */ 649e2a70cffSBoris Popov mb_put_uint16le(mbp, *len); /* MinCount (only indicates blocking) */ 650e2a70cffSBoris Popov mb_put_uint32le(mbp, (unsigned)*len >> 16); /* MaxCountHigh */ 651e2a70cffSBoris Popov mb_put_uint16le(mbp, *len); /* Remaining ("obsolete") */ 652e2a70cffSBoris Popov mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */ 653e2a70cffSBoris Popov smb_rq_wend(rqp); 654e2a70cffSBoris Popov smb_rq_bstart(rqp); 655e2a70cffSBoris Popov smb_rq_bend(rqp); 656e2a70cffSBoris Popov do { 657e2a70cffSBoris Popov error = smb_rq_simple(rqp); 658e2a70cffSBoris Popov if (error) 659e2a70cffSBoris Popov break; 660e2a70cffSBoris Popov smb_rq_getreply(rqp, &mdp); 661e2a70cffSBoris Popov off = SMB_HDRLEN; 662e2a70cffSBoris Popov md_get_uint8(mdp, &wc); 663e2a70cffSBoris Popov off++; 664e2a70cffSBoris Popov if (wc != 12) { 665e2a70cffSBoris Popov error = EBADRPC; 666e2a70cffSBoris Popov break; 667e2a70cffSBoris Popov } 668e2a70cffSBoris Popov md_get_uint8(mdp, NULL); 669e2a70cffSBoris Popov off++; 670e2a70cffSBoris Popov md_get_uint8(mdp, NULL); 671e2a70cffSBoris Popov off++; 672e2a70cffSBoris Popov md_get_uint16le(mdp, NULL); 673e2a70cffSBoris Popov off += 2; 674e2a70cffSBoris Popov md_get_uint16le(mdp, NULL); 675e2a70cffSBoris Popov off += 2; 676e2a70cffSBoris Popov md_get_uint16le(mdp, NULL); /* data compaction mode */ 677e2a70cffSBoris Popov off += 2; 678e2a70cffSBoris Popov md_get_uint16le(mdp, NULL); 679e2a70cffSBoris Popov off += 2; 680e2a70cffSBoris Popov md_get_uint16le(mdp, &residlo); 681e2a70cffSBoris Popov off += 2; 682e2a70cffSBoris Popov md_get_uint16le(mdp, &doff); /* data offset */ 683e2a70cffSBoris Popov off += 2; 684e2a70cffSBoris Popov md_get_uint16le(mdp, &residhi); 685e2a70cffSBoris Popov off += 2; 686e2a70cffSBoris Popov resid = (residhi << 16) | residlo; 687e2a70cffSBoris Popov md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); 688e2a70cffSBoris Popov off += 4*2; 689e2a70cffSBoris Popov md_get_uint16le(mdp, NULL); /* ByteCount */ 690e2a70cffSBoris Popov off += 2; 691e2a70cffSBoris Popov if (doff > off) /* pad byte(s)? */ 692e2a70cffSBoris Popov md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM); 693e2a70cffSBoris Popov if (resid == 0) { 694e2a70cffSBoris Popov *rresid = resid; 695e2a70cffSBoris Popov break; 696e2a70cffSBoris Popov } 697e2a70cffSBoris Popov error = md_get_uio(mdp, uio, resid); 698e2a70cffSBoris Popov if (error) 699e2a70cffSBoris Popov break; 700e2a70cffSBoris Popov *rresid = resid; 701e2a70cffSBoris Popov } while(0); 702e2a70cffSBoris Popov smb_rq_done(rqp); 703e2a70cffSBoris Popov return (error); 704e2a70cffSBoris Popov } 705e2a70cffSBoris Popov 706e2a70cffSBoris Popov static __inline int 707e2a70cffSBoris Popov smb_smb_writex(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, 708e2a70cffSBoris Popov struct uio *uio, struct smb_cred *scred) 709e2a70cffSBoris Popov { 710e2a70cffSBoris Popov struct smb_rq *rqp; 711e2a70cffSBoris Popov struct mbchain *mbp; 712e2a70cffSBoris Popov struct mdchain *mdp; 713e2a70cffSBoris Popov int error; 714e2a70cffSBoris Popov u_int8_t wc; 715e2a70cffSBoris Popov u_int16_t resid; 716e2a70cffSBoris Popov 717e2a70cffSBoris Popov error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp); 718e2a70cffSBoris Popov if (error) 719e2a70cffSBoris Popov return (error); 720e2a70cffSBoris Popov smb_rq_getrequest(rqp, &mbp); 721e2a70cffSBoris Popov smb_rq_wstart(rqp); 722e2a70cffSBoris Popov mb_put_uint8(mbp, 0xff); /* no secondary command */ 723e2a70cffSBoris Popov mb_put_uint8(mbp, 0); /* MBZ */ 724e2a70cffSBoris Popov mb_put_uint16le(mbp, 0); /* offset to secondary */ 725e2a70cffSBoris Popov mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); 726e2a70cffSBoris Popov mb_put_uint32le(mbp, uio->uio_offset); 727e2a70cffSBoris Popov mb_put_uint32le(mbp, 0); /* MBZ (timeout) */ 728e2a70cffSBoris Popov mb_put_uint16le(mbp, 0); /* !write-thru */ 729e2a70cffSBoris Popov mb_put_uint16le(mbp, 0); 730e2a70cffSBoris Popov *len = min(SSTOVC(ssp)->vc_wxmax, *len); 731e2a70cffSBoris Popov mb_put_uint16le(mbp, (unsigned)*len >> 16); 732e2a70cffSBoris Popov mb_put_uint16le(mbp, *len); 733e2a70cffSBoris Popov mb_put_uint16le(mbp, 64); /* data offset from header start */ 734e2a70cffSBoris Popov mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */ 735e2a70cffSBoris Popov smb_rq_wend(rqp); 736e2a70cffSBoris Popov smb_rq_bstart(rqp); 737e2a70cffSBoris Popov do { 738e2a70cffSBoris Popov mb_put_uint8(mbp, 0xee); /* mimic xp pad byte! */ 739e2a70cffSBoris Popov error = mb_put_uio(mbp, uio, *len); 740e2a70cffSBoris Popov if (error) 741e2a70cffSBoris Popov break; 742e2a70cffSBoris Popov smb_rq_bend(rqp); 743e2a70cffSBoris Popov error = smb_rq_simple(rqp); 744e2a70cffSBoris Popov if (error) 745e2a70cffSBoris Popov break; 746e2a70cffSBoris Popov smb_rq_getreply(rqp, &mdp); 747e2a70cffSBoris Popov md_get_uint8(mdp, &wc); 748e2a70cffSBoris Popov if (wc != 6) { 749e2a70cffSBoris Popov error = EBADRPC; 750e2a70cffSBoris Popov break; 751e2a70cffSBoris Popov } 752e2a70cffSBoris Popov md_get_uint8(mdp, NULL); 753e2a70cffSBoris Popov md_get_uint8(mdp, NULL); 754e2a70cffSBoris Popov md_get_uint16le(mdp, NULL); 755e2a70cffSBoris Popov md_get_uint16le(mdp, &resid); 756e2a70cffSBoris Popov *rresid = resid; 757e2a70cffSBoris Popov } while(0); 758e2a70cffSBoris Popov 759e2a70cffSBoris Popov smb_rq_done(rqp); 760e2a70cffSBoris Popov return (error); 761e2a70cffSBoris Popov } 762e2a70cffSBoris Popov 763e2a70cffSBoris Popov static __inline int 764681a5bbeSBoris Popov smb_smb_read(struct smb_share *ssp, u_int16_t fid, 765681a5bbeSBoris Popov int *len, int *rresid, struct uio *uio, struct smb_cred *scred) 766681a5bbeSBoris Popov { 767681a5bbeSBoris Popov struct smb_rq *rqp; 768681a5bbeSBoris Popov struct mbchain *mbp; 769681a5bbeSBoris Popov struct mdchain *mdp; 770681a5bbeSBoris Popov u_int16_t resid, bc; 771681a5bbeSBoris Popov u_int8_t wc; 772681a5bbeSBoris Popov int error, rlen, blksz; 773681a5bbeSBoris Popov 774e2a70cffSBoris Popov if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) 775e2a70cffSBoris Popov return (smb_smb_readx(ssp, fid, len, rresid, uio, scred)); 776e2a70cffSBoris Popov 777681a5bbeSBoris Popov error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp); 778681a5bbeSBoris Popov if (error) 779681a5bbeSBoris Popov return error; 780681a5bbeSBoris Popov 781681a5bbeSBoris Popov blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; 782681a5bbeSBoris Popov rlen = *len = min(blksz, *len); 783681a5bbeSBoris Popov 784681a5bbeSBoris Popov smb_rq_getrequest(rqp, &mbp); 785681a5bbeSBoris Popov smb_rq_wstart(rqp); 786681a5bbeSBoris Popov mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); 787681a5bbeSBoris Popov mb_put_uint16le(mbp, rlen); 788681a5bbeSBoris Popov mb_put_uint32le(mbp, uio->uio_offset); 789681a5bbeSBoris Popov mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff)); 790681a5bbeSBoris Popov smb_rq_wend(rqp); 791681a5bbeSBoris Popov smb_rq_bstart(rqp); 792681a5bbeSBoris Popov smb_rq_bend(rqp); 793681a5bbeSBoris Popov do { 794681a5bbeSBoris Popov error = smb_rq_simple(rqp); 795681a5bbeSBoris Popov if (error) 796681a5bbeSBoris Popov break; 797681a5bbeSBoris Popov smb_rq_getreply(rqp, &mdp); 798681a5bbeSBoris Popov md_get_uint8(mdp, &wc); 799681a5bbeSBoris Popov if (wc != 5) { 800681a5bbeSBoris Popov error = EBADRPC; 801681a5bbeSBoris Popov break; 802681a5bbeSBoris Popov } 803681a5bbeSBoris Popov md_get_uint16le(mdp, &resid); 804681a5bbeSBoris Popov md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); 805681a5bbeSBoris Popov md_get_uint16le(mdp, &bc); 806681a5bbeSBoris Popov md_get_uint8(mdp, NULL); /* ignore buffer type */ 807681a5bbeSBoris Popov md_get_uint16le(mdp, &resid); 808681a5bbeSBoris Popov if (resid == 0) { 809681a5bbeSBoris Popov *rresid = resid; 810681a5bbeSBoris Popov break; 811681a5bbeSBoris Popov } 812681a5bbeSBoris Popov error = md_get_uio(mdp, uio, resid); 813681a5bbeSBoris Popov if (error) 814681a5bbeSBoris Popov break; 815681a5bbeSBoris Popov *rresid = resid; 816681a5bbeSBoris Popov } while(0); 817681a5bbeSBoris Popov smb_rq_done(rqp); 818681a5bbeSBoris Popov return error; 819681a5bbeSBoris Popov } 820681a5bbeSBoris Popov 821681a5bbeSBoris Popov int 822681a5bbeSBoris Popov smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio, 823681a5bbeSBoris Popov struct smb_cred *scred) 824681a5bbeSBoris Popov { 825681a5bbeSBoris Popov int tsize, len, resid; 826681a5bbeSBoris Popov int error = 0; 827681a5bbeSBoris Popov 828681a5bbeSBoris Popov tsize = uio->uio_resid; 829681a5bbeSBoris Popov while (tsize > 0) { 8302d494bc6SMatt Jacob resid = 0; 831681a5bbeSBoris Popov len = tsize; 832681a5bbeSBoris Popov error = smb_smb_read(ssp, fid, &len, &resid, uio, scred); 833681a5bbeSBoris Popov if (error) 834681a5bbeSBoris Popov break; 835681a5bbeSBoris Popov tsize -= resid; 836681a5bbeSBoris Popov if (resid < len) 837681a5bbeSBoris Popov break; 838681a5bbeSBoris Popov } 839681a5bbeSBoris Popov return error; 840681a5bbeSBoris Popov } 841681a5bbeSBoris Popov 842681a5bbeSBoris Popov static __inline int 843681a5bbeSBoris Popov smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, 844681a5bbeSBoris Popov struct uio *uio, struct smb_cred *scred) 845681a5bbeSBoris Popov { 846681a5bbeSBoris Popov struct smb_rq *rqp; 847681a5bbeSBoris Popov struct mbchain *mbp; 848681a5bbeSBoris Popov struct mdchain *mdp; 849681a5bbeSBoris Popov u_int16_t resid; 850681a5bbeSBoris Popov u_int8_t wc; 851681a5bbeSBoris Popov int error, blksz; 852681a5bbeSBoris Popov 853e2a70cffSBoris Popov if (*len && SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) 854e2a70cffSBoris Popov return (smb_smb_writex(ssp, fid, len, rresid, uio, scred)); 855e2a70cffSBoris Popov 856681a5bbeSBoris Popov blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; 857681a5bbeSBoris Popov if (blksz > 0xffff) 858681a5bbeSBoris Popov blksz = 0xffff; 859681a5bbeSBoris Popov 860681a5bbeSBoris Popov resid = *len = min(blksz, *len); 861681a5bbeSBoris Popov 862681a5bbeSBoris Popov error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp); 863681a5bbeSBoris Popov if (error) 864681a5bbeSBoris Popov return error; 865681a5bbeSBoris Popov smb_rq_getrequest(rqp, &mbp); 866681a5bbeSBoris Popov smb_rq_wstart(rqp); 867681a5bbeSBoris Popov mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); 868681a5bbeSBoris Popov mb_put_uint16le(mbp, resid); 869681a5bbeSBoris Popov mb_put_uint32le(mbp, uio->uio_offset); 870681a5bbeSBoris Popov mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff)); 871681a5bbeSBoris Popov smb_rq_wend(rqp); 872681a5bbeSBoris Popov smb_rq_bstart(rqp); 873681a5bbeSBoris Popov mb_put_uint8(mbp, SMB_DT_DATA); 874681a5bbeSBoris Popov mb_put_uint16le(mbp, resid); 875681a5bbeSBoris Popov do { 876681a5bbeSBoris Popov error = mb_put_uio(mbp, uio, resid); 877681a5bbeSBoris Popov if (error) 878681a5bbeSBoris Popov break; 879681a5bbeSBoris Popov smb_rq_bend(rqp); 880681a5bbeSBoris Popov error = smb_rq_simple(rqp); 881681a5bbeSBoris Popov if (error) 882681a5bbeSBoris Popov break; 883681a5bbeSBoris Popov smb_rq_getreply(rqp, &mdp); 884681a5bbeSBoris Popov md_get_uint8(mdp, &wc); 885681a5bbeSBoris Popov if (wc != 1) { 886681a5bbeSBoris Popov error = EBADRPC; 887681a5bbeSBoris Popov break; 888681a5bbeSBoris Popov } 889681a5bbeSBoris Popov md_get_uint16le(mdp, &resid); 890681a5bbeSBoris Popov *rresid = resid; 891681a5bbeSBoris Popov } while(0); 892681a5bbeSBoris Popov smb_rq_done(rqp); 893681a5bbeSBoris Popov return error; 894681a5bbeSBoris Popov } 895681a5bbeSBoris Popov 896681a5bbeSBoris Popov int 897681a5bbeSBoris Popov smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio, 898681a5bbeSBoris Popov struct smb_cred *scred) 899681a5bbeSBoris Popov { 900681a5bbeSBoris Popov int error = 0, len, tsize, resid; 901681a5bbeSBoris Popov struct uio olduio; 902681a5bbeSBoris Popov 903681a5bbeSBoris Popov tsize = uio->uio_resid; 904681a5bbeSBoris Popov olduio = *uio; 905681a5bbeSBoris Popov while (tsize > 0) { 9062d494bc6SMatt Jacob resid = 0; 907681a5bbeSBoris Popov len = tsize; 908681a5bbeSBoris Popov error = smb_smb_write(ssp, fid, &len, &resid, uio, scred); 909681a5bbeSBoris Popov if (error) 910681a5bbeSBoris Popov break; 911681a5bbeSBoris Popov if (resid < len) { 912681a5bbeSBoris Popov error = EIO; 913681a5bbeSBoris Popov break; 914681a5bbeSBoris Popov } 915681a5bbeSBoris Popov tsize -= resid; 916681a5bbeSBoris Popov } 917681a5bbeSBoris Popov if (error) { 9186cd9842fSBoris Popov /* 9196cd9842fSBoris Popov * Errors can happen on the copyin, the rpc, etc. So they 9206cd9842fSBoris Popov * imply resid is unreliable. The only safe thing is 9216cd9842fSBoris Popov * to pretend zero bytes made it. We needn't restore the 9226cd9842fSBoris Popov * iovs because callers don't depend on them in error 9236cd9842fSBoris Popov * paths - uio_resid and uio_offset are what matter. 9246cd9842fSBoris Popov */ 925681a5bbeSBoris Popov *uio = olduio; 926681a5bbeSBoris Popov } 927681a5bbeSBoris Popov return error; 928681a5bbeSBoris Popov } 929681a5bbeSBoris Popov 930681a5bbeSBoris Popov int 931681a5bbeSBoris Popov smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred) 932681a5bbeSBoris Popov { 933681a5bbeSBoris Popov struct smb_rq *rqp; 934681a5bbeSBoris Popov struct mbchain *mbp; 935681a5bbeSBoris Popov int error; 936681a5bbeSBoris Popov 937681a5bbeSBoris Popov error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp); 938681a5bbeSBoris Popov if (error) 939681a5bbeSBoris Popov return error; 940681a5bbeSBoris Popov mbp = &rqp->sr_rq; 941681a5bbeSBoris Popov smb_rq_wstart(rqp); 942681a5bbeSBoris Popov mb_put_uint16le(mbp, 1); 943681a5bbeSBoris Popov smb_rq_wend(rqp); 944681a5bbeSBoris Popov smb_rq_bstart(rqp); 945681a5bbeSBoris Popov mb_put_uint32le(mbp, 0); 946681a5bbeSBoris Popov smb_rq_bend(rqp); 947681a5bbeSBoris Popov error = smb_rq_simple(rqp); 948681a5bbeSBoris Popov SMBSDEBUG("%d\n", error); 949681a5bbeSBoris Popov smb_rq_done(rqp); 950681a5bbeSBoris Popov return error; 951681a5bbeSBoris Popov } 952