/* * smbrdr_handle_setup * * Allocates a buffer for sending/receiving a SMB request. * Initialize a smb_msgbuf structure with the allocated buffer. * Setup given handle (srh) with the specified information. * * Returns: * * NT_STATUS_NO_MEMORY not enough memory * NT_STATUS_SUCCESS successful */ static DWORD smbrdr_handle_setup(smbrdr_handle_t *srh, unsigned char cmd, struct sdb_session *session, struct sdb_logon *logon, struct sdb_netuse *netuse) { srh->srh_buf = (unsigned char *)malloc(SMBRDR_REQ_BUFSZ); if (srh->srh_buf == NULL) return (NT_STATUS_NO_MEMORY); bzero(srh->srh_buf, SMBRDR_REQ_BUFSZ); srh->srh_mbflags = (session->remote_caps & CAP_UNICODE) ? SMB_MSGBUF_UNICODE : 0; smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, SMBRDR_REQ_BUFSZ, srh->srh_mbflags); srh->srh_cmd = cmd; srh->srh_session = session; srh->srh_user = logon; srh->srh_tree = netuse; return (NT_STATUS_SUCCESS); }
/* * smbrdr_rcv * * Receive a SMB response and decode the packet header. * * "Implementing CIFS" book, SMB requests always have an even sequence * number and replies always have an odd. * * With the original code, if the SMB Redirector skip the counter increment * in the event of any failure during SmbSessionSetupAndX, it causes the * domain controller to fail the next SMB request(odd sequence number) * with ACCESS_DENIED. * * Smbrdr module should use the same sequence number (i.e. ssc_seqnum of the * SMB Sign context) for generating the MAC signature for all incoming * responses per SmbTransact request. Otherwise, the validation will fail. * It is now fixed by decrementing the sequence number prior to validating * the subsequent responses for a single request. * * Returns: * * status code returned by smbrdr_hdr_process() * NT_STATUS_UNEXPECTED_NETWORK_ERROR receive failed * NT_STATUS_SUCCESS successful */ DWORD smbrdr_rcv(smbrdr_handle_t *srh, int is_first_rsp) { smb_hdr_t smb_hdr; DWORD status; int rc; smb_sign_ctx_t *sign_ctx = &srh->srh_session->sign_ctx; rc = nb_rcv(srh->srh_session->sock, srh->srh_buf, SMBRDR_REQ_BUFSZ, 0); if (rc < 0) { smb_mac_inc_seqnum(sign_ctx); smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_rcv[%d]: receive failed (%d)", srh->srh_cmd, rc); return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); } smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, rc, srh->srh_mbflags); status = smbrdr_hdr_process(srh, &smb_hdr); if (status != NT_STATUS_SUCCESS) { smb_mac_inc_seqnum(sign_ctx); smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_rcv[%d]: failed (%s)", srh->srh_cmd, xlate_nt_status(status)); return (status); } if (!is_first_rsp) smb_mac_dec_seqnum(sign_ctx); if (!smbrdr_sign_chk(sign_ctx, &srh->srh_mbuf, smb_hdr.extra.extra.security_sig)) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_rcv[%d]: bad signature", srh->srh_cmd); return (NT_STATUS_INVALID_NETWORK_RESPONSE); } return (NT_STATUS_SUCCESS); }
/* * smbrdr_exchange * * Send the SMB packet pointed by the given handle over * network. Receive the response and decode the packet header. * * From "Implementing CIFS" book, SMB requests always have an even sequence * number and replies always have an odd. * * With the original code, if the SMB Redirector skips the counter increment * in the event of any failure during SmbSessionSetupAndX, it causes the * domain controller to fail the next SMB request(odd sequence number) * with ACCESS_DENIED. * * Returns: * * status code returned by smbrdr_hdr_process() * NT_STATUS_INTERNAL_ERROR crypto framework failure * NT_STATUS_UNEXPECTED_NETWORK_ERROR send/receive failed * NT_STATUS_SUCCESS successful */ DWORD smbrdr_exchange(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr, long timeout) { smb_sign_ctx_t *sign_ctx; smb_msgbuf_t *mb; DWORD status; int rc; smbrdr_lock_transport(); mb = &srh->srh_mbuf; sign_ctx = &srh->srh_session->sign_ctx; if (smbrdr_sign(sign_ctx, mb) != SMBAUTH_SUCCESS) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_exchange[%d]: signing failed", srh->srh_cmd); smbrdr_unlock_transport(); return (NT_STATUS_INTERNAL_ERROR); } rc = nb_exchange(srh->srh_session->sock, srh->srh_buf, smb_msgbuf_used(mb), srh->srh_buf, SMBRDR_REQ_BUFSZ, timeout); if (rc < 0) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_exchange[%d]: failed (%d)", srh->srh_cmd, rc); if (srh->srh_cmd != SMB_COM_ECHO) { /* * Since SMB echo is used to check the session * status then don't destroy the session if it's * SMB echo. */ srh->srh_session->state = SDB_SSTATE_STALE; } smb_mac_inc_seqnum(sign_ctx); smbrdr_unlock_transport(); return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); } /* initialize for processing response */ smb_msgbuf_init(mb, srh->srh_buf, rc, srh->srh_mbflags); status = smbrdr_hdr_process(srh, smb_hdr); if (status != NT_STATUS_SUCCESS) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_exchange[%d]: failed (%s)", srh->srh_cmd, xlate_nt_status(status)); smb_mac_inc_seqnum(sign_ctx); smbrdr_unlock_transport(); return (status); } /* Signature validation */ if (!smbrdr_sign_chk(sign_ctx, mb, smb_hdr->extra.extra.security_sig)) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_exchange[%d]: bad signature", srh->srh_cmd); smbrdr_unlock_transport(); return (NT_STATUS_INVALID_NETWORK_RESPONSE); } smbrdr_unlock_transport(); return (NT_STATUS_SUCCESS); }
/* * smb_trans2_mbc_encode * * This function encodes the mbc for one directory entry. * * The function returns -1 when the max data requested by client * is reached. If the entry is valid and successful encoded, 0 * will be returned; otherwise, 1 will be returned. * * We always null terminate the filename. The space for the null * is included in the maxdata calculation and is therefore included * in the next_entry_offset. namelen is the unterminated length of * the filename. For levels except STANDARD and EA_SIZE, if the * filename is ascii the name length returned to the client should * include the null terminator. Otherwise the length returned to * the client should not include the terminator. * * Returns: 0 - data successfully encoded * 1 - client request's maxdata limit reached * -1 - error */ static int smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa, smb_fileinfo_t *fileinfo, smb_find_args_t *args) { int namelen, shortlen; uint32_t next_entry_offset; uint32_t dsize32, asize32; uint32_t mb_flags = 0; uint32_t resume_key; char buf83[26]; smb_msgbuf_t mb; namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name); if (namelen == -1) return (-1); /* * If ascii the filename length returned to the client should * include the null terminator for levels except STANDARD and * EASIZE. */ if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) { if ((args->fa_infolev != SMB_INFO_STANDARD) && (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE)) namelen += 1; } next_entry_offset = args->fa_maxdata + namelen; if (MBC_ROOM_FOR(&xa->rep_data_mb, (args->fa_maxdata + namelen)) == 0) return (1); mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0; dsize32 = (fileinfo->fi_size > UINT_MAX) ? UINT_MAX : (uint32_t)fileinfo->fi_size; asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ? UINT_MAX : (uint32_t)fileinfo->fi_alloc_size; resume_key = fileinfo->fi_cookie; if (smbd_use_resume_keys == 0) resume_key = 0; /* * This switch handles all the "information levels" (formats) * that we support. Note that all formats have the file name * placed after some fixed-size data, and the code to write * the file name is factored out at the end of this switch. */ switch (args->fa_infolev) { case SMB_INFO_STANDARD: if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS) (void) smb_mbc_encodef(&xa->rep_data_mb, "l", resume_key); (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwb", sr, smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec), dsize32, asize32, fileinfo->fi_dosattr, namelen); break; case SMB_INFO_QUERY_EA_SIZE: if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS) (void) smb_mbc_encodef(&xa->rep_data_mb, "l", resume_key); (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb", sr, smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec), dsize32, asize32, fileinfo->fi_dosattr, 0L, /* EA Size */ namelen); break; case SMB_FIND_FILE_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqll", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen); break; case SMB_FIND_FILE_FULL_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L); break; case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll4.q", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, fileinfo->fi_nodeid); break; case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: bzero(buf83, sizeof (buf83)); smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83), mb_flags); if (smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname) < 0) { smb_msgbuf_term(&mb); return (-1); } shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname); (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24c", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, shortlen, buf83); smb_msgbuf_term(&mb); break; case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO: bzero(buf83, sizeof (buf83)); smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83), mb_flags); if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_shortname) < 0) { smb_msgbuf_term(&mb); return (-1); } shortlen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_shortname); (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24c2.q", sr, next_entry_offset, resume_key, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, shortlen, buf83, fileinfo->fi_nodeid); smb_msgbuf_term(&mb); break; case SMB_FIND_FILE_NAMES_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%lll", sr, next_entry_offset, resume_key, namelen); break; default: /* invalid info. level */ return (-1); } /* * At this point we have written all the fixed-size data * for the specified info. level, and we're about to put * the file name string in the message. We may later * need the offset in the trans2 data where this string * is placed, so save the message position now. Note: * We also need to account for the alignment padding * that may precede the unicode string. */ args->fa_lno = xa->rep_data_mb.chain_offset; if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0 && (args->fa_lno & 1) != 0) args->fa_lno++; (void) smb_mbc_encodef(&xa->rep_data_mb, "%u", sr, fileinfo->fi_name); return (0); }
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); }
/* * This function builds a response for a NetShareEnum RAP request. * List of shares is scanned twice. In the first round the total number * of shares which their OEM name is shorter than 13 chars (esi->es_ntotal) * and also the number of shares that fit in the given buffer are calculated. * In the second round the shares data are encoded in the buffer. * * The data associated with each share has two parts, a fixed size part and * a variable size part which is share's comment. The outline of the response * buffer is so that fixed part for all the shares will appear first and follows * with the comments for all those shares and that's why the data cannot be * encoded in one round without unnecessarily complicating the code. */ void smb_kshare_enum(smb_server_t *sv, smb_enumshare_info_t *esi) { smb_avl_t *share_avl; smb_avl_cursor_t cursor; smb_kshare_t *shr; int remained; uint16_t infolen = 0; uint16_t cmntlen = 0; uint16_t sharelen; uint16_t clen; uint32_t cmnt_offs; smb_msgbuf_t info_mb; smb_msgbuf_t cmnt_mb; boolean_t autohome_added = B_FALSE; if (!smb_export_isready(sv)) { esi->es_ntotal = esi->es_nsent = 0; esi->es_datasize = 0; return; } esi->es_ntotal = esi->es_nsent = 0; remained = esi->es_bufsize; share_avl = &sv->sv_export.e_share_avl; /* Do the necessary calculations in the first round */ smb_avl_iterinit(share_avl, &cursor); while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) { if (shr->shr_oemname == NULL) { smb_avl_release(share_avl, shr); continue; } if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) { if (esi->es_posix_uid == shr->shr_uid) { autohome_added = B_TRUE; } else { smb_avl_release(share_avl, shr); continue; } } esi->es_ntotal++; if (remained <= 0) { smb_avl_release(share_avl, shr); continue; } clen = strlen(shr->shr_cmnt) + 1; sharelen = SHARE_INFO_1_SIZE + clen; if (sharelen <= remained) { infolen += SHARE_INFO_1_SIZE; cmntlen += clen; } remained -= sharelen; smb_avl_release(share_avl, shr); } esi->es_datasize = infolen + cmntlen; smb_msgbuf_init(&info_mb, (uint8_t *)esi->es_buf, infolen, 0); smb_msgbuf_init(&cmnt_mb, (uint8_t *)esi->es_buf + infolen, cmntlen, 0); cmnt_offs = infolen; /* Encode the data in the second round */ smb_avl_iterinit(share_avl, &cursor); autohome_added = B_FALSE; while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) { if (shr->shr_oemname == NULL) { smb_avl_release(share_avl, shr); continue; } if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) { if (esi->es_posix_uid == shr->shr_uid) { autohome_added = B_TRUE; } else { smb_avl_release(share_avl, shr); continue; } } if (smb_msgbuf_encode(&info_mb, "13c.wl", shr->shr_oemname, shr->shr_type, cmnt_offs) < 0) { smb_avl_release(share_avl, shr); break; } if (smb_msgbuf_encode(&cmnt_mb, "s", shr->shr_cmnt) < 0) { smb_avl_release(share_avl, shr); break; } cmnt_offs += strlen(shr->shr_cmnt) + 1; esi->es_nsent++; smb_avl_release(share_avl, shr); } smb_msgbuf_term(&info_mb); smb_msgbuf_term(&cmnt_mb); }
/* * smb_trans2_mbc_encode * * This function encodes the mbc for one directory entry. * * The function returns -1 when the max data requested by client * is reached. If the entry is valid and successful encoded, 0 * will be returned; otherwise, 1 will be returned. * * We always null terminate the filename. The space for the null * is included in the maxdata calculation and is therefore included * in the next_entry_offset. namelen is the unterminated length of * the filename. For levels except STANDARD and EA_SIZE, if the * filename is ascii the name length returned to the client should * include the null terminator. Otherwise the length returned to * the client should not include the terminator. * * Returns: 0 - data successfully encoded * 1 - client request's maxdata limit reached * -1 - error */ static int smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa, smb_fileinfo_t *fileinfo, smb_find_args_t *args) { int namelen, shortlen, buflen; uint32_t next_entry_offset; uint32_t dsize32, asize32; uint32_t mb_flags = 0; char buf83[26]; char *tmpbuf; smb_msgbuf_t mb; namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name); if (namelen == -1) return (-1); next_entry_offset = args->fa_maxdata + namelen; if (MBC_ROOM_FOR(&xa->rep_data_mb, (args->fa_maxdata + namelen)) == 0) return (1); /* * If ascii the filename length returned to the client should * include the null terminator for levels except STANDARD and * EASIZE. */ if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) { if ((args->fa_infolev != SMB_INFO_STANDARD) && (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE)) namelen += 1; } mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0; dsize32 = (fileinfo->fi_size > UINT_MAX) ? UINT_MAX : (uint32_t)fileinfo->fi_size; asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ? UINT_MAX : (uint32_t)fileinfo->fi_alloc_size; switch (args->fa_infolev) { case SMB_INFO_STANDARD: if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS) (void) smb_mbc_encodef(&xa->rep_data_mb, "l", fileinfo->fi_cookie); (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwbu", sr, smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec), dsize32, asize32, fileinfo->fi_dosattr, namelen, fileinfo->fi_name); break; case SMB_INFO_QUERY_EA_SIZE: if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS) (void) smb_mbc_encodef(&xa->rep_data_mb, "l", fileinfo->fi_cookie); /* * Unicode filename should NOT be aligned. Encode ('u') * into a temporary buffer, then encode buffer as a * byte stream ('#c'). * Regardless of whether unicode or ascii, a single * termination byte is used. */ buflen = namelen + sizeof (smb_wchar_t); tmpbuf = kmem_zalloc(buflen, KM_SLEEP); smb_msgbuf_init(&mb, (uint8_t *)tmpbuf, buflen, mb_flags); if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_name) < 0) { smb_msgbuf_term(&mb); kmem_free(tmpbuf, buflen); return (-1); } tmpbuf[namelen] = '\0'; (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb#c", sr, smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec), smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec), dsize32, asize32, fileinfo->fi_dosattr, 0L, /* EA Size */ namelen, namelen + 1, tmpbuf); smb_msgbuf_term(&mb); kmem_free(tmpbuf, buflen); break; case SMB_FIND_FILE_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqllu", sr, next_entry_offset, fileinfo->fi_cookie, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, fileinfo->fi_name); break; case SMB_FIND_FILE_FULL_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllu", sr, next_entry_offset, fileinfo->fi_cookie, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, fileinfo->fi_name); break; case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll4.qu", sr, next_entry_offset, fileinfo->fi_cookie, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, fileinfo->fi_nodeid, fileinfo->fi_name); break; case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: bzero(buf83, sizeof (buf83)); smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83), mb_flags); if (smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname) < 0) { smb_msgbuf_term(&mb); return (-1); } shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname); (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24cu", sr, next_entry_offset, fileinfo->fi_cookie, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, shortlen, buf83, fileinfo->fi_name); smb_msgbuf_term(&mb); break; case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO: bzero(buf83, sizeof (buf83)); smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83), mb_flags); if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_shortname) < 0) { smb_msgbuf_term(&mb); return (-1); } shortlen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_shortname); (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24c2.qu", sr, next_entry_offset, fileinfo->fi_cookie, &fileinfo->fi_crtime, &fileinfo->fi_atime, &fileinfo->fi_mtime, &fileinfo->fi_ctime, fileinfo->fi_size, fileinfo->fi_alloc_size, fileinfo->fi_dosattr, namelen, 0L, shortlen, buf83, fileinfo->fi_nodeid, fileinfo->fi_name); smb_msgbuf_term(&mb); break; case SMB_FIND_FILE_NAMES_INFO: (void) smb_mbc_encodef(&xa->rep_data_mb, "%lllu", sr, next_entry_offset, fileinfo->fi_cookie, namelen, fileinfo->fi_name); break; } return (0); }