/* * Starts the creation of a new printer file, which will be deleted * automatically once it has been closed and printed. * * SetupLength is the number of bytes in the first part of the resulting * print spool file which contains printer-specific control strings. * * Mode can have the following values: * 0 Text mode. The server may optionally * expand tabs to a series of spaces. * 1 Graphics mode. No conversion of data * should be done by the server. * * IdentifierString can be used by the server to provide some sort of * per-client identifying component to the print file. * * When the file is closed, it will be sent to the spooler and printed. */ smb_sdrc_t smb_pre_open_print_file(smb_request_t *sr) { struct open_param *op = &sr->arg.open; char *path; char *identifier; uint32_t new_id; uint16_t setup; uint16_t mode; int rc; static uint32_t tmp_id = 10000; bzero(op, sizeof (sr->arg.open)); rc = smbsr_decode_vwv(sr, "ww", &setup, &mode); if (rc == 0) rc = smbsr_decode_data(sr, "%S", sr, &identifier); if (rc == 0) { path = smb_srm_zalloc(sr, MAXPATHLEN); op->fqi.fq_path.pn_path = path; new_id = atomic_inc_32_nv(&tmp_id); (void) snprintf(path, MAXPATHLEN, "%s%05u", identifier, new_id); } op->create_disposition = FILE_OVERWRITE_IF; op->create_options = FILE_NON_DIRECTORY_FILE; DTRACE_SMB_2(op__OpenPrintFile__start, smb_request_t *, sr, struct open_param *, op); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
smb_sdrc_t smb_com_create_temporary(smb_request_t *sr) { static uint16_t tmp_id = 10000; struct open_param *op = &sr->arg.open; char name[SMB_CREATE_NAMEBUF_SZ]; char *buf; uint16_t bcc; ++tmp_id; bcc = 1; /* null terminator */ bcc += snprintf(name, SMB_CREATE_NAMEBUF_SZ, "tt%05d.tmp", tmp_id); buf = smb_srm_zalloc(sr, MAXPATHLEN); (void) snprintf(buf, MAXPATHLEN, "%s\\%s", op->fqi.fq_path.pn_path, name); op->fqi.fq_path.pn_path = buf; if (smb_common_create(sr) != NT_STATUS_SUCCESS) return (SDRC_ERROR); if (smbsr_encode_result(sr, 1, VAR_BCC, "bww%S", 1, sr->smb_fid, VAR_BCC, sr, name)) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * smb_pathname_strdup * * Duplicate NULL terminated string s. * * The new string is allocated using request specific storage and will * be free'd when the sr is destroyed. */ static char * smb_pathname_strdup(smb_request_t *sr, const char *s) { char *s2; size_t n; n = strlen(s) + 1; s2 = smb_srm_zalloc(sr, n); (void) strlcpy(s2, s, n); return (s2); }
smb_sdrc_t smb_com_echo(struct smb_request *sr) { unsigned short necho; unsigned short nbytes; unsigned short i; struct mbuf_chain reply; char *data; if (smbsr_decode_vwv(sr, "w", &necho) != 0) return (SDRC_ERROR); nbytes = sr->smb_bcc; data = smb_srm_zalloc(sr, nbytes); if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data)) return (SDRC_ERROR); for (i = 1; i <= necho; ++i) { MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes); (void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT, sr->first_smb_com, sr->smb_rcls, sr->smb_reh, sr->smb_err, sr->smb_flg | SMB_FLAGS_REPLY, sr->smb_flg2, sr->smb_pid_high, sr->smb_sig, sr->smb_tid, sr->smb_pid, sr->smb_uid, sr->smb_mid); (void) smb_mbc_encodef(&reply, "bww#c", 1, i, nbytes, nbytes, data); if (sr->session->signing.flags & SMB_SIGNING_ENABLED) smb_sign_reply(sr, &reply); (void) smb_session_send(sr->session, 0, &reply); } return (SDRC_NO_REPLY); }
smb_sdrc_t smb_pre_negotiate(smb_request_t *sr) { smb_kmod_cfg_t *skc; smb_arg_negotiate_t *negprot; int dialect; int pos; int rc = 0; skc = &sr->session->s_cfg; negprot = smb_srm_zalloc(sr, sizeof (smb_arg_negotiate_t)); negprot->ni_index = -1; sr->sr_negprot = negprot; for (pos = 0; smbsr_decode_data_avail(sr); pos++) { if (smbsr_decode_data(sr, "%L", sr, &negprot->ni_name) != 0) { smbsr_error(sr, 0, ERRSRV, ERRerror); rc = -1; break; } if ((dialect = smb_xlate_dialect(negprot->ni_name)) < 0) continue; /* * Conditionally recognize the SMB2 dialects. */ if (dialect >= DIALECT_SMB2002 && skc->skc_max_protocol < SMB_VERS_2_BASE) continue; if (negprot->ni_dialect < dialect) { negprot->ni_dialect = dialect; negprot->ni_index = pos; } } DTRACE_SMB_2(op__Negotiate__start, smb_request_t *, sr, smb_arg_negotiate_t, negprot); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
smb_sdrc_t smb_pre_write_raw(smb_request_t *sr) { smb_rw_param_t *param; uint32_t off_low; uint32_t timeout; uint32_t off_high; uint16_t datalen; uint16_t total; int rc; param = smb_srm_zalloc(sr, sizeof (smb_rw_param_t)); sr->arg.rw = param; param->rw_magic = SMB_RW_MAGIC; if (sr->smb_wct == 12) { rc = smbsr_decode_vwv(sr, "ww2.llw4.ww", &sr->smb_fid, &total, &off_low, &timeout, ¶m->rw_mode, &datalen, ¶m->rw_dsoff); param->rw_offset = (uint64_t)off_low; param->rw_dsoff -= 59; } else { rc = smbsr_decode_vwv(sr, "ww2.llw4.wwl", &sr->smb_fid, &total, &off_low, &timeout, ¶m->rw_mode, &datalen, ¶m->rw_dsoff, &off_high); param->rw_offset = ((uint64_t)off_high << 32) | off_low; param->rw_dsoff -= 63; } param->rw_count = (uint32_t)datalen; param->rw_total = (uint32_t)total; param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset; DTRACE_SMB_2(op__WriteRaw__start, smb_request_t *, sr, smb_rw_param_t *, sr->arg.rw); smb_rwx_rwenter(&sr->session->s_lock, RW_WRITER); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
smb_sdrc_t smb_com_echo(struct smb_request *sr) { unsigned short necho; unsigned short nbytes; unsigned short i; struct mbuf_chain reply; char *data; uint16_t pid_hi, pid_lo; pid_hi = sr->smb_pid >> 16; pid_lo = (uint16_t)sr->smb_pid; if (smbsr_decode_vwv(sr, "w", &necho) != 0) return (SDRC_ERROR); /* * Don't let the client fool us into doing * more work than is "reasonable". */ if (necho > smb_max_echo) necho = smb_max_echo; nbytes = sr->smb_bcc; data = smb_srm_zalloc(sr, nbytes); if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data)) return (SDRC_ERROR); for (i = 1; i <= necho; ++i) { /* * According to [MS-CIFS] 3.3.5.32 echo is * subject to cancellation. */ if (sr->sr_state != SMB_REQ_STATE_ACTIVE) break; MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes); (void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT, sr->first_smb_com, sr->smb_rcls, sr->smb_reh, sr->smb_err, sr->smb_flg | SMB_FLAGS_REPLY, sr->smb_flg2, pid_hi, sr->smb_sig, sr->smb_tid, pid_lo, sr->smb_uid, sr->smb_mid); (void) smb_mbc_encodef(&reply, "bww#c", 1, i, nbytes, nbytes, data); if (sr->session->signing.flags & SMB_SIGNING_ENABLED) smb_sign_reply(sr, &reply); (void) smb_session_send(sr->session, 0, &reply); delay(MSEC_TO_TICK(100)); } return (SDRC_NO_REPLY); }
smb_sdrc_t smb2_read(smb_request_t *sr) { smb_ofile_t *of = NULL; smb_vdb_t *vdb = NULL; struct mbuf *m = NULL; uint16_t StructSize; uint8_t Padding; uint8_t DataOff; uint32_t Length; uint64_t Offset; smb2fid_t smb2fid; uint32_t MinCount; uint32_t Channel; uint32_t Remaining; uint16_t ChanInfoOffset; uint16_t ChanInfoLength; uint32_t XferCount; uint32_t status; int rc = 0; /* * SMB2 Read request */ rc = smb_mbc_decodef( &sr->smb_data, "wb.lqqqlllww", &StructSize, /* w */ &Padding, /* b. */ &Length, /* l */ &Offset, /* q */ &smb2fid.persistent, /* q */ &smb2fid.temporal, /* q */ &MinCount, /* l */ &Channel, /* l */ &Remaining, /* l */ &ChanInfoOffset, /* w */ &ChanInfoLength); /* w */ if (rc) return (SDRC_ERROR); if (StructSize != 49) return (SDRC_ERROR); status = smb2sr_lookup_fid(sr, &smb2fid); if (status) { smb2sr_put_error(sr, status); return (SDRC_SUCCESS); } of = sr->fid_ofile; if (Length > smb2_max_rwsize) { smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER); return (SDRC_SUCCESS); } if (MinCount > Length) MinCount = Length; /* This is automatically free'd. */ vdb = smb_srm_zalloc(sr, sizeof (*vdb)); vdb->vdb_tag = 0; vdb->vdb_uio.uio_iov = &vdb->vdb_iovec[0]; vdb->vdb_uio.uio_iovcnt = MAX_IOVEC; vdb->vdb_uio.uio_resid = Length; vdb->vdb_uio.uio_loffset = (offset_t)Offset; vdb->vdb_uio.uio_segflg = UIO_SYSSPACE; vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT; sr->raw_data.max_bytes = Length; m = smb_mbuf_allocate(&vdb->vdb_uio); switch (of->f_tree->t_res_type & STYPE_MASK) { case STYPE_DISKTREE: if (!smb_node_is_dir(of->f_node)) { /* Check for conflicting locks. */ rc = smb_lock_range_access(sr, of->f_node, Offset, Length, B_FALSE); if (rc) { rc = ERANGE; break; } } rc = smb_fsop_read(sr, of->f_cr, of->f_node, &vdb->vdb_uio); break; case STYPE_IPC: rc = smb_opipe_read(sr, &vdb->vdb_uio); break; default: case STYPE_PRINTQ: rc = EACCES; break; } /* How much data we moved. */ XferCount = Length - vdb->vdb_uio.uio_resid; sr->raw_data.max_bytes = XferCount; smb_mbuf_trim(m, XferCount); MBC_ATTACH_MBUF(&sr->raw_data, m); /* * Checking the error return _after_ dealing with * the returned data so that if m was allocated, * it will be free'd via sr->raw_data cleanup. */ if (rc) { smb2sr_put_errno(sr, rc); return (SDRC_SUCCESS); } /* * SMB2 Read reply */ DataOff = SMB2_HDR_SIZE + 16; rc = smb_mbc_encodef( &sr->reply, "wb.lllC", 17, /* StructSize */ /* w */ DataOff, /* b. */ XferCount, /* l */ 0, /* DataRemaining */ /* l */ 0, /* reserved */ /* l */ &sr->raw_data); /* C */ if (rc) return (SDRC_ERROR); mutex_enter(&of->f_mutex); of->f_seek_pos = Offset + XferCount; mutex_exit(&of->f_mutex); return (SDRC_SUCCESS); }
smb_sdrc_t smb2_session_setup(smb_request_t *sr) { smb_arg_sessionsetup_t *sinfo; uint16_t StructureSize; uint8_t Flags; uint8_t SecurityMode; uint32_t Capabilities; /* ignored - see above */ uint32_t Channel; uint16_t SecBufOffset; uint16_t SecBufLength; uint64_t PrevSessionId; uint16_t SessionFlags; uint32_t status; int skip; int rc = 0; sinfo = smb_srm_zalloc(sr, sizeof (smb_arg_sessionsetup_t)); sr->sr_ssetup = sinfo; rc = smb_mbc_decodef( &sr->smb_data, "wbbllwwq", &StructureSize, /* w */ &Flags, /* b */ &SecurityMode, /* b */ &Capabilities, /* l */ &Channel, /* l */ &SecBufOffset, /* w */ &SecBufLength, /* w */ &PrevSessionId); /* q */ if (rc) return (SDRC_ERROR); /* * We're normally positioned at the security buffer now, * but there could be some padding before it. */ skip = (SecBufOffset + sr->smb2_cmd_hdr) - sr->smb_data.chain_offset; if (skip < 0) return (SDRC_ERROR); if (skip > 0) (void) smb_mbc_decodef(&sr->smb_data, "#.", skip); /* * Get the security buffer */ sinfo->ssi_iseclen = SecBufLength; sinfo->ssi_isecblob = smb_srm_zalloc(sr, sinfo->ssi_iseclen); rc = smb_mbc_decodef(&sr->smb_data, "#c", sinfo->ssi_iseclen, sinfo->ssi_isecblob); if (rc) return (SDRC_ERROR); /* * The real auth. work happens in here. */ status = smb_authenticate_ext(sr); SecBufOffset = SMB2_HDR_SIZE + 8; SecBufLength = sinfo->ssi_oseclen; SessionFlags = 0; switch (status) { case NT_STATUS_SUCCESS: /* Authenticated */ if (sr->uid_user->u_flags & SMB_USER_FLAG_GUEST) SessionFlags |= SMB2_SESSION_FLAG_IS_GUEST; if (sr->uid_user->u_flags & SMB_USER_FLAG_ANON) SessionFlags |= SMB2_SESSION_FLAG_IS_NULL; smb2_ss_adjust_credits(sr); break; /* * This is not really an error, but tells the client * it should send another session setup request. * Not smb2_put_error because we send a payload. */ case NT_STATUS_MORE_PROCESSING_REQUIRED: sr->smb2_status = status; break; default: SecBufLength = 0; sr->smb2_status = status; break; } /* * SMB2 Session Setup reply */ rc = smb_mbc_encodef( &sr->reply, "wwww#c", 9, /* StructSize */ /* w */ SessionFlags, /* w */ SecBufOffset, /* w */ SecBufLength, /* w */ SecBufLength, /* # */ sinfo->ssi_osecblob); /* c */ if (rc) return (SDRC_ERROR); return (SDRC_SUCCESS); }
smb_sdrc_t smb_com_negotiate(smb_request_t *sr) { smb_session_t *session = sr->session; smb_arg_negotiate_t *negprot = sr->sr_negprot; uint16_t secmode; uint32_t sesskey; char *nbdomain; uint8_t *wcbuf; int wclen; smb_msgbuf_t mb; int rc; if (session->s_state != SMB_SESSION_STATE_ESTABLISHED) { /* The protocol has already been negotiated. */ smbsr_error(sr, 0, ERRSRV, ERRerror); return (SDRC_ERROR); } /* * Special case for negotiating SMB2 from SMB1. The client * includes the "SMB 2..." dialects in the SMB1 negotiate, * and if SMB2 is enabled, we choose one of those and then * send an SMB2 reply to that SMB1 request. Yes, it's very * strange, but this SMB1 request can have an SMB2 reply! * To accomplish this, we let the SMB2 code send the reply * and return the special code SDRC_NO_REPLY to the SMB1 * dispatch logic so it will NOT send an SMB1 reply. * (Or possibly send an SMB1 error reply.) */ if (negprot->ni_dialect >= DIALECT_SMB2002) { rc = smb1_negotiate_smb2(sr); ASSERT(rc == SDRC_NO_REPLY || rc == SDRC_DROP_VC || rc == SDRC_ERROR); return (rc); } session->secmode = NEGOTIATE_ENCRYPT_PASSWORDS | NEGOTIATE_USER_SECURITY; secmode = session->secmode; sesskey = session->sesskey; negprot->ni_servertime.tv_sec = gethrestime_sec(); negprot->ni_servertime.tv_nsec = 0; negprot->ni_tzcorrection = sr->sr_gmtoff / 60; negprot->ni_maxmpxcount = sr->sr_cfg->skc_maxworkers; negprot->ni_keylen = SMB_CHALLENGE_SZ; bcopy(&session->challenge_key, negprot->ni_key, SMB_CHALLENGE_SZ); nbdomain = sr->sr_cfg->skc_nbdomain; /* * UNICODE support is required for long share names, * long file names and streams. Note: CAP_RAW_MODE * is not supported because it does nothing to help * modern clients and causes nasty complications. */ negprot->ni_capabilities = CAP_LARGE_FILES | CAP_UNICODE | CAP_NT_SMBS | CAP_STATUS32 | CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS | CAP_LOCK_AND_READ | CAP_RPC_REMOTE_APIS | CAP_LARGE_READX | CAP_LARGE_WRITEX | CAP_DFS; if (smb_cap_passthru) negprot->ni_capabilities |= CAP_INFOLEVEL_PASSTHRU; else cmn_err(CE_NOTE, "smbsrv: cap passthru is %s", (negprot->ni_capabilities & CAP_INFOLEVEL_PASSTHRU) ? "enabled" : "disabled"); switch (negprot->ni_dialect) { case PC_NETWORK_PROGRAM_1_0: /* core */ (void) ksocket_setsockopt(session->sock, SOL_SOCKET, SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf, sizeof (smb_dos_tcp_rcvbuf), CRED()); rc = smbsr_encode_result(sr, 1, 0, "bww", 1, negprot->ni_index, 0); break; case Windows_for_Workgroups_3_1a: case PCLAN1_0: case MICROSOFT_NETWORKS_1_03: case MICROSOFT_NETWORKS_3_0: case LANMAN1_0: case LM1_2X002: case DOS_LM1_2X002: (void) ksocket_setsockopt(session->sock, SOL_SOCKET, SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf, sizeof (smb_dos_tcp_rcvbuf), CRED()); sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK; rc = smbsr_encode_result(sr, 13, VAR_BCC, "bwwwwwwlYww2.w#c", 13, /* wct */ negprot->ni_index, /* dialect index */ secmode, /* security mode */ SMB_DOS_MAXBUF, /* max buffer size */ 1, /* max MPX */ 1, /* max VCs */ 0, /* read/write raw */ sesskey, /* session key */ negprot->ni_servertime.tv_sec, /* server date/time */ negprot->ni_tzcorrection, (uint16_t)negprot->ni_keylen, /* encryption key length */ /* reserved field handled 2. */ VAR_BCC, (int)negprot->ni_keylen, negprot->ni_key); /* encryption key */ break; case DOS_LANMAN2_1: case LANMAN2_1: (void) ksocket_setsockopt(session->sock, SOL_SOCKET, SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf, sizeof (smb_dos_tcp_rcvbuf), CRED()); sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK; rc = smbsr_encode_result(sr, 13, VAR_BCC, "bwwwwwwlYww2.w#cs", 13, /* wct */ negprot->ni_index, /* dialect index */ secmode, /* security mode */ SMB_DOS_MAXBUF, /* max buffer size */ 1, /* max MPX */ 1, /* max VCs */ 0, /* read/write raw */ sesskey, /* session key */ negprot->ni_servertime.tv_sec, /* server date/time */ negprot->ni_tzcorrection, (uint16_t)negprot->ni_keylen, /* encryption key length */ /* reserved field handled 2. */ VAR_BCC, (int)negprot->ni_keylen, negprot->ni_key, /* encryption key */ nbdomain); break; case NT_LM_0_12: (void) ksocket_setsockopt(session->sock, SOL_SOCKET, SO_RCVBUF, (const void *)&smb_nt_tcp_rcvbuf, sizeof (smb_nt_tcp_rcvbuf), CRED()); /* * Allow SMB signatures if using encrypted passwords */ if ((secmode & NEGOTIATE_ENCRYPT_PASSWORDS) && sr->sr_cfg->skc_signing_enable) { secmode |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED; if (sr->sr_cfg->skc_signing_required) secmode |= NEGOTIATE_SECURITY_SIGNATURES_REQUIRED; session->secmode = secmode; } /* * Does the client want Extended Security? * (and if we have it enabled) * If so, handle as if a different dialect. */ if ((sr->smb_flg2 & SMB_FLAGS2_EXT_SEC) && smb_cap_ext_sec) goto NT_LM_0_12_ext_sec; sr->smb_flg2 &= ~SMB_FLAGS2_EXT_SEC; /* * nbdomain is not expected to be aligned. * Use temporary buffer to avoid alignment padding */ wclen = smb_wcequiv_strlen(nbdomain) + sizeof (smb_wchar_t); wcbuf = smb_srm_zalloc(sr, wclen); smb_msgbuf_init(&mb, wcbuf, wclen, SMB_MSGBUF_UNICODE); if (smb_msgbuf_encode(&mb, "U", nbdomain) < 0) { smb_msgbuf_term(&mb); smbsr_error(sr, 0, ERRSRV, ERRerror); return (SDRC_ERROR); } rc = smbsr_encode_result(sr, 17, VAR_BCC, "bwbwwllllTwbw#c#c", 17, /* wct */ negprot->ni_index, /* dialect index */ secmode, /* security mode */ negprot->ni_maxmpxcount, /* max MPX */ 1, /* max VCs */ (DWORD)smb_maxbufsize, /* max buffer size */ 0xFFFF, /* max raw size */ sesskey, /* session key */ negprot->ni_capabilities, &negprot->ni_servertime, /* system time */ negprot->ni_tzcorrection, negprot->ni_keylen, /* encryption key length */ VAR_BCC, (int)negprot->ni_keylen, negprot->ni_key, /* encryption key */ wclen, wcbuf); /* nbdomain (unicode) */ smb_msgbuf_term(&mb); break; NT_LM_0_12_ext_sec: /* * This is the "Extended Security" variant of * dialect NT_LM_0_12. */ negprot->ni_capabilities |= CAP_EXTENDED_SECURITY; rc = smbsr_encode_result(sr, 17, VAR_BCC, "bwbwwllllTwbw#c#c", 17, /* wct */ negprot->ni_index, /* dialect index */ secmode, /* security mode */ negprot->ni_maxmpxcount, /* max MPX */ 1, /* max VCs */ (DWORD)smb_maxbufsize, /* max buffer size */ 0xFFFF, /* max raw size */ sesskey, /* session key */ negprot->ni_capabilities, &negprot->ni_servertime, /* system time */ negprot->ni_tzcorrection, 0, /* encryption key length (MBZ) */ VAR_BCC, UUID_LEN, sr->sr_cfg->skc_machine_uuid, sr->sr_cfg->skc_negtok_len, sr->sr_cfg->skc_negtok); break; default: rc = smbsr_encode_result(sr, 1, 0, "bww", 1, -1, 0); break; } if (rc != 0) return (SDRC_ERROR); /* * Save the agreed dialect. Note that the state is also * used to detect and reject attempts to re-negotiate. */ session->dialect = negprot->ni_dialect; session->s_state = SMB_SESSION_STATE_NEGOTIATED; /* Allow normal SMB1 requests now. */ session->newrq_func = smb1sr_newrq; return (SDRC_SUCCESS); }