static ssize_t netbios_session_get_next_packet(netbios_session *s) { ssize_t res; size_t total, sofar; assert(s != NULL && s->packet != NULL && s->socket && s->state > 0); // Only get packet header and analyze it to get only needed number of bytes // needed for the packet. This will prevent losing a part of next packet res = recv(s->socket, (void *)(s->packet), sizeof(netbios_session_packet), 0); if (res <= 0) { BDSM_perror("netbios_session_packet_recv: "); return -1; } else if ((size_t)res != sizeof(netbios_session_packet)) { BDSM_dbg("netbios_session_packet_recv: incorrect size for received packet: %ld bytes", res); return -1; } total = ntohs(s->packet->length); total |= (s->packet->flags & 0x01) << 16; sofar = 0; if (total + sizeof(netbios_session_packet) > s->packet_payload_size) if (!session_buffer_realloc(s, total + sizeof(netbios_session_packet))) return -1; //BDSM_dbg("Total = %ld, sofar = %ld\n", total, sofar); while (sofar < total) { res = recv(s->socket, (void *)(s->packet) + 4 + sofar, total - sofar, 0); //BDSM_dbg("Total = %ld, sofar = %ld, res = %ld\n", total, sofar, res); if (res <= 0) { BDSM_perror("netbios_session_packet_recv: "); return -1; } sofar += res; } if (sofar > total) { BDSM_dbg("netbios_session_packet_recv: Packet size mismatch (%ld/%ld)\n", sofar, total); return -1; } return sofar; }
int smb_session_login_spnego(smb_session *s, const char *domain, const char *user, const char *password) { int res; assert(s != NULL && domain != NULL && user != NULL && password != NULL); // Clear User ID that might exists from previous authentication attempt s->srv.uid = 0; if (init_asn1(s) != DSM_SUCCESS) return DSM_ERROR_GENERIC; if ((res = negotiate(s, domain)) != DSM_SUCCESS) goto error; if ((res = challenge(s)) != DSM_SUCCESS) goto error; res = auth(s, domain, user, password); clean_asn1(s); return res; error: BDSM_dbg("login_spnego Interrupted\n"); clean_asn1(s); return res; }
static void asn1_display_error(const char *where, int errcode) { // Avoids warning when not in debug mode (void)where; (void)errcode; BDSM_dbg("%s error: %s\n", where, asn1_strerror(errcode)); }
smb_file *smb_find(smb_session *s, smb_tid tid, const char *pattern) { smb_message *msg; smb_trans2_req *tr2; smb_tr2_find2 *find; size_t pattern_len, msg_len; int res; assert(s != NULL && pattern != NULL && tid); pattern_len = strlen(pattern) + 1; msg_len = sizeof(smb_trans2_req) + sizeof(smb_tr2_find2); msg_len += pattern_len * 2 + 3; msg = smb_message_new(SMB_CMD_TRANS2, msg_len); smb_message_set_default_flags(msg); msg->packet->header.tid = tid; tr2 = (smb_trans2_req *)msg->packet->payload; tr2->wct = 15; tr2->total_param_count = pattern_len * 2 + sizeof(smb_tr2_find2); tr2->max_param_count = 10; // ?? Why not the same or 12 ? tr2->max_data_count = 0xffff; tr2->param_count = tr2->total_param_count; tr2->param_offset = 68; // Offset of find_first_params in packet; tr2->data_count = 0; tr2->data_offset = 88; // Offset of pattern in packet tr2->setup_count = 1; tr2->cmd = SMB_TR2_FIND_FIRST; tr2->bct = sizeof(smb_tr2_find2) + pattern_len * 2 + 3; find = (smb_tr2_find2 *) tr2->payload; find->attrs = SMB_FIND2_ATTR_DEFAULT; find->count = 1366; // ?? find->flags = SMB_FIND2_FLAG_DEFAULT; find->interest = 0x0104; // 'Find file both directory info' smb_message_advance(msg, sizeof(smb_trans2_req)); smb_message_advance(msg, sizeof(smb_tr2_find2)); smb_message_put_utf16(msg, "", pattern, pattern_len); // Adds padding at the end if necessary. if (tr2->bct % 4) smb_message_advance(msg, 4 - tr2->bct % 4); res = smb_session_send_msg(s, msg); smb_message_destroy(msg); if (!res) { BDSM_dbg("Unable to query pattern: %s\n", pattern); return (NULL); } if ((msg = smb_tr2_recv(s)) == NULL) return (NULL); return (smb_find_parse(msg)); }
static int init_asn1(smb_session *s) { int res; assert(s != NULL); if (s->spnego_asn1 != NULL) return DSM_ERROR_GENERIC; res = asn1_array2tree(spnego_asn1_conf, &s->spnego_asn1, NULL); if (res != ASN1_SUCCESS) { asn1_display_error("init_asn1", res); return DSM_ERROR_GENERIC; } else { BDSM_dbg("init_asn1: ASN.1 parser initialized\n"); return DSM_SUCCESS; } }
static int negotiate(smb_session *s, const char *domain) { smb_message *msg = NULL; smb_session_xsec_req req; smb_buffer ntlm; ASN1_TYPE token; int res, der_size = 128; char der[128], err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; msg = smb_message_new(SMB_CMD_SETUP); if (!msg) return DSM_ERROR_GENERIC; // this struct will be set at the end when we know the payload size SMB_MSG_ADVANCE_PKT(msg, smb_session_xsec_req); asn1_create_element(s->spnego_asn1, "SPNEGO.GSSAPIContextToken", &token); res = asn1_write_value(token, "thisMech", spnego_oid, 1); if (res != ASN1_SUCCESS) goto error; res = asn1_write_value(token, "spnego", "negTokenInit", 1); if (res != ASN1_SUCCESS) goto error; res = asn1_write_value(token, "spnego.negTokenInit.mechTypes", "NEW", 1); if (res != ASN1_SUCCESS) goto error; res = asn1_write_value(token, "spnego.negTokenInit.mechTypes.?1", ntlmssp_oid, 1); if (res != ASN1_SUCCESS) goto error; res = asn1_write_value(token, "spnego.negTokenInit.reqFlags", NULL, 0); if (res != ASN1_SUCCESS) goto error; res = asn1_write_value(token, "spnego.negTokenInit.mechListMIC", NULL, 0); if (res != ASN1_SUCCESS) goto error; smb_ntlmssp_negotiate(domain, domain, &ntlm); res = asn1_write_value(token, "spnego.negTokenInit.mechToken", ntlm.data, ntlm.size); smb_buffer_free(&ntlm); if (res != ASN1_SUCCESS) goto error; res = asn1_der_coding(token, "", der, &der_size, err_desc); if (res != ASN1_SUCCESS) { smb_message_destroy(msg); BDSM_dbg("Encoding error: %s", err_desc); return DSM_ERROR_GENERIC; } smb_message_append(msg, der, der_size); smb_message_put_utf16(msg, SMB_OS, strlen(SMB_OS)); smb_message_put16(msg, 0); smb_message_put_utf16(msg, SMB_LANMAN, strlen(SMB_LANMAN)); smb_message_put16(msg, 0); smb_message_put16(msg, 0); SMB_MSG_INIT_PKT_ANDX(req); req.wct = 12; req.max_buffer = SMB_SESSION_MAX_BUFFER; req.mpx_count = 16; req.vc_count = 1; req.caps = s->srv.caps; req.session_key = s->srv.session_key; req.xsec_blob_size = der_size; req.payload_size = msg->cursor - sizeof(smb_session_xsec_req); SMB_MSG_INSERT_PKT(msg, 0, req); asn1_delete_structure(&token); if (!smb_session_send_msg(s, msg)) { smb_message_destroy(msg); BDSM_dbg("Unable to send Session Setup AndX (NTLMSSP_NEGOTIATE) message\n"); return DSM_ERROR_NETWORK; } smb_message_destroy(msg); return DSM_SUCCESS; error: asn1_display_error("smb_session_login negotiate()", res); smb_message_destroy(msg); return DSM_ERROR_GENERIC; }
static int auth(smb_session *s, const char *domain, const char *user, const char *password) { smb_message *msg = NULL, resp; smb_session_xsec_req req; smb_buffer ntlm; ASN1_TYPE token; int res, der_size = 512; char der[512], err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; msg = smb_message_new(SMB_CMD_SETUP); if (!msg) return DSM_ERROR_GENERIC; // this struct will be set at the end when we know the payload size SMB_MSG_ADVANCE_PKT(msg, smb_session_xsec_req); asn1_create_element(s->spnego_asn1, "SPNEGO.NegotiationToken", &token); // Select a response message type res = asn1_write_value(token, "", "negTokenResp", 1); if (res != ASN1_SUCCESS) goto error; // Delete all optionnal field except 'ResponseToken' res = asn1_write_value(token, "negTokenResp.negResult", NULL, 0); if (res != ASN1_SUCCESS) goto error; res = asn1_write_value(token, "negTokenResp.supportedMech", NULL, 0); if (res != ASN1_SUCCESS) goto error; res = asn1_write_value(token, "negTokenResp.mechListMIC", NULL, 0); if (res != ASN1_SUCCESS) goto error; smb_ntlmssp_response(s->srv.challenge, s->srv.ts - 4200, domain, domain, user, password, &s->xsec_target, &ntlm); res = asn1_write_value(token, "negTokenResp.responseToken", ntlm.data, ntlm.size); smb_buffer_free(&ntlm); if (res != ASN1_SUCCESS) goto error; res = asn1_der_coding(token, "", der, &der_size, err_desc); if (res != ASN1_SUCCESS) { smb_message_destroy(msg); BDSM_dbg("Encoding error: %s", err_desc); return DSM_ERROR_GENERIC; } smb_message_append(msg, der, der_size); if (msg->cursor % 2) smb_message_put8(msg, 0); smb_message_put_utf16(msg, SMB_OS, strlen(SMB_OS)); smb_message_put16(msg, 0); smb_message_put_utf16(msg, SMB_LANMAN, strlen(SMB_LANMAN)); smb_message_put16(msg, 0); smb_message_put16(msg, 0); // Empty PDC name SMB_MSG_INIT_PKT_ANDX(req); req.wct = 12; req.max_buffer = SMB_SESSION_MAX_BUFFER; req.mpx_count = 16; // XXX ? req.vc_count = 1; req.caps = s->srv.caps; // XXX caps & our_caps_mask req.session_key = s->srv.session_key; req.xsec_blob_size = der_size; req.payload_size = msg->cursor - sizeof(smb_session_xsec_req); SMB_MSG_INSERT_PKT(msg, 0, req); asn1_delete_structure(&token); if (!smb_session_send_msg(s, msg)) { smb_message_destroy(msg); BDSM_dbg("Unable to send Session Setup AndX (NTLMSSP_AUTH) message\n"); return DSM_ERROR_NETWORK; } smb_message_destroy(msg); if (smb_session_recv_msg(s, &resp) == 0) return DSM_ERROR_NETWORK; if (!smb_session_check_nt_status(s, &resp)) return DSM_ERROR_NT; else { smb_session_xsec_resp *r = (smb_session_xsec_resp *)resp.packet->payload; if (r->action & 0x0001) s->guest = true; s->srv.uid = resp.packet->header.uid; s->logged = true; return DSM_SUCCESS; } error: asn1_display_error("smb_session_login auth()", res); smb_message_destroy(msg); return DSM_ERROR_GENERIC; }
static int challenge(smb_session *s) { char err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; char resp_token[256]; smb_message msg; smb_session_xsec_resp *resp; smb_ntlmssp_challenge *challenge; ASN1_TYPE token; int res, resp_token_size = 256; assert(s != NULL); if (smb_session_recv_msg(s, &msg) == 0) { BDSM_dbg("spnego challenge(): Unable to receive message\n"); return DSM_ERROR_NETWORK; } if (msg.packet->header.status != NT_STATUS_MORE_PROCESSING_REQUIRED) { BDSM_dbg("spnego challenge(): Bad status (0x%x)\n", msg.packet->header.status); return DSM_ERROR_GENERIC; } resp = (smb_session_xsec_resp *)msg.packet->payload; asn1_create_element(s->spnego_asn1, "SPNEGO.NegotiationToken", &token); res = asn1_der_decoding(&token, resp->payload, resp->xsec_blob_size, err_desc); if (res != ASN1_SUCCESS) { asn1_delete_structure(&token); asn1_display_error("NegTokenResp parsing", res); BDSM_dbg("Parsing error detail: %s\n", err_desc); return DSM_ERROR_GENERIC; } // XXX Check the value of "negTokenResp.negResult" res = asn1_read_value(token, "negTokenResp.responseToken", resp_token, &resp_token_size); asn1_delete_structure(&token); if (res != ASN1_SUCCESS) { asn1_display_error("NegTokenResp read responseToken", res); return DSM_ERROR_GENERIC; } // We got the server challenge, yeaaah. challenge = (smb_ntlmssp_challenge *)resp_token; if (smb_buffer_alloc(&s->xsec_target, challenge->tgt_len) == 0) return DSM_ERROR_GENERIC; memcpy(s->xsec_target.data, challenge->data + challenge->tgt_offset - sizeof(smb_ntlmssp_challenge), s->xsec_target.size); s->srv.challenge = challenge->challenge; s->srv.uid = msg.packet->header.uid; BDSM_dbg("Server challenge is 0x%"PRIx64"\n", s->srv.challenge); return DSM_SUCCESS; }
smb_file *smb_fstat(smb_session *s, smb_tid tid, const char *path) { smb_message *msg, reply; smb_trans2_req *tr2; smb_trans2_resp *tr2_resp; smb_tr2_query *query; smb_tr2_path_info *info; smb_file *file; size_t path_len, msg_len; int res; assert(s != NULL && path != NULL && tid); path_len = strlen(path) + 1; msg_len = sizeof(smb_trans2_req) + sizeof(smb_tr2_query); msg_len += path_len * 2 + 3; // +3 for eventual padding msg = smb_message_new(SMB_CMD_TRANS2, msg_len); smb_message_set_default_flags(msg); msg->packet->header.tid = tid; tr2 = (smb_trans2_req *)msg->packet->payload; tr2->wct = 15; tr2->total_param_count = path_len * 2 + sizeof(smb_tr2_query); tr2->max_param_count = 2; // ?? Why not the same or 12 ? tr2->max_data_count = 0xffff; tr2->param_count = tr2->total_param_count; tr2->param_offset = 68; // Offset of find_first_params in packet; tr2->data_count = 0; tr2->data_offset = 96; // Offset of pattern in packet tr2->setup_count = 1; tr2->cmd = SMB_TR2_QUERY_PATH; tr2->bct = sizeof(smb_tr2_query) + path_len * 2 + 3; query = (smb_tr2_query *)tr2->payload; query->interest = 0x0107; // Query File All Info smb_message_advance(msg, sizeof(smb_trans2_req)); smb_message_advance(msg, sizeof(smb_tr2_query)); smb_message_put_utf16(msg, "", path, path_len); // Adds padding at the end if necessary. if (msg->cursor % 4) { int padding = 4 - msg->cursor % 4; tr2->bct += padding; for (int i = 0; i < padding; i++) smb_message_put8(msg, 0); } res = smb_session_send_msg(s, msg); smb_message_destroy(msg); if (!res) { BDSM_dbg("Unable to query pattern: %s\n", path); return (NULL); } if (!smb_session_recv_msg(s, &reply) || reply.packet->header.status != NT_STATUS_SUCCESS) { BDSM_dbg("Unable to recv msg or failure for %s\n", path); return (NULL); } tr2_resp = (smb_trans2_resp *)reply.packet->payload; info = (smb_tr2_path_info *)(tr2_resp->payload + 4); //+4 is padding file = calloc(1, sizeof(smb_file)); assert(file != NULL); file->name_len = smb_from_utf16((const char *)info->name, info->name_len, &file->name); file->name[info->name_len] = 0; file->created = info->created; file->accessed = info->accessed; file->written = info->written; file->changed = info->changed; file->alloc_size = info->alloc_size; file->size = info->size; file->attr = info->attr; file->is_dir = info->is_dir; return (file); }
// We should normally implement SCERPC and SRVSVC to perform a share list. But // since these two protocols have no other use for us, we'll do it the trash way // PS: Worst function _EVER_. I don't understand a bit myself int smb_share_get_list(smb_session *s, smb_share_list *list, size_t *pcount) { smb_message *req, resp; smb_trans_req trans; smb_tid ipc_tid; smb_fd srvscv_fd; uint16_t rpc_len; size_t res, frag_len_cursor; ssize_t count; int ret; assert(s != NULL && list != NULL); if(s != NULL && list != NULL) { *list = NULL; if ((ret = smb_tree_connect(s, "IPC$", &ipc_tid)) != DSM_SUCCESS) return ret; if ((ret = smb_fopen(s, ipc_tid, "\\srvsvc", SMB_MOD_READ | SMB_MOD_WRITE, &srvscv_fd)) != DSM_SUCCESS) return ret; //// Phase 1: // We bind a context or whatever for DCE/RPC req = smb_message_new(SMD_CMD_TRANS); if (!req) { ret = DSM_ERROR_GENERIC; goto error; } req->packet->header.tid = ipc_tid; rpc_len = 0xffff; SMB_MSG_INIT_PKT(trans); trans.wct = 16; trans.total_data_count = 72; trans.max_data_count = rpc_len; trans.param_offset = 84; trans.data_count = 72; trans.data_offset = 84; trans.setup_count = 2; trans.pipe_function = 0x26; trans.fid = SMB_FD_FID(srvscv_fd); trans.bct = 89; SMB_MSG_PUT_PKT(req, trans); smb_message_put8(req, 0); // Padding smb_message_put_utf16(req, "\\PIPE\\", strlen("\\PIPE\\") + 1); smb_message_put16(req, 0); // Padding to be aligned with wtf boundary :-/ // Now we'll 'build' the DCE/RPC Packet. This basically a copycat // from wireshark values. smb_message_put8(req, 5); // version major smb_message_put8(req, 0); // minor smb_message_put8(req, 0x0b); // Packet type = 'bind' smb_message_put8(req, 0x03); // Packet flags = ?? smb_message_put32(req, 0x10); // Representation = little endian/ASCII. Damn smb_message_put16(req, 72); // Data len again smb_message_put16(req, 0); // Auth len ? smb_message_put32(req, 19); // Call ID ? smb_message_put16(req, rpc_len); // Max Xmit size smb_message_put16(req, rpc_len); // Max Recv size smb_message_put32(req, 0); // Assoc group ? smb_message_put32(req, 1); // Num Ctx Item // Adding the CtxItem, whatever could that be smb_message_put16(req, 0); // ContextID smb_message_put16(req, 1); // Num Trans Item // SRVSVC UUID const uint8_t uuid_e[8] = {0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88}; smb_message_put_uuid(req, 0x4b324fc8, 0x1670, 0x01d3, uuid_e); smb_message_put16(req, 3); // Version smb_message_put16(req, 0); // Minor // Another UUID const uint8_t uuid_e2[8] = {0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60}; smb_message_put_uuid(req, 0x8a885d04, 0x1ceb, 0x11c9, uuid_e2); smb_message_put32(req, 2); // Another version // Let's send this ugly pile of shit over the network ! res = smb_session_send_msg(s, req); smb_message_destroy(req); if (!res) { ret = DSM_ERROR_NETWORK; goto error; } // Is the server throwing pile of shit back at me ? res = smb_session_recv_msg(s, &resp); if (!res || resp.packet->payload[68]) { BDSM_dbg("Bind call failed: 0x%hhx (reason = 0x%hhx)\n", resp.packet->payload[68], resp.packet->payload[70]); ret = DSM_ERROR_NETWORK; goto error; } //// Phase 2: // Now we have the 'bind' done (regarless of what it is), we'll call // NetShareEnumAll req = smb_message_new(SMD_CMD_TRANS); if (!req) { ret = DSM_ERROR_GENERIC; goto error; } req->packet->header.tid = ipc_tid; // this struct will be set at the end when we know the data size SMB_MSG_ADVANCE_PKT(req, smb_trans_req); smb_message_put8(req, 0); // Padding smb_message_put_utf16(req, "\\PIPE\\", strlen("\\PIPE\\") + 1); smb_message_put16(req, 0); // Padding // Now we'll 'build' the DCE/RPC Packet. This basically a copycat // from wireshark values. smb_message_put8(req, 5); // version major smb_message_put8(req, 0); // minor smb_message_put8(req, 0); // Packet type = 'request' smb_message_put8(req, 0x03); // Packet flags = ?? smb_message_put32(req, 0x10); // Representation = little endian/ASCII. Damn // Let's save the cursor here to update that later frag_len_cursor = req->cursor; smb_message_put16(req, 0); // Data len again (frag length) smb_message_put16(req, 0); // Auth len ? smb_message_put32(req, 12); // Call ID ? smb_message_put32(req, 64); // Alloc hint ? smb_message_put16(req, 0); // Context ID ? smb_message_put16(req, 15); // OpNum = NetShareEnumAll // Pointer to server UNC smb_message_put32(req, 0x00020000); // Referent ID ? smb_message_put32(req, strlen(s->srv.name) + 1); // Max count smb_message_put32(req, 0); // Offset smb_message_put32(req, strlen(s->srv.name) + 1); // Actual count // The server name, supposed to be downcased smb_message_put_utf16(req, s->srv.name, strlen(s->srv.name) + 1); if ((strlen(s->srv.name) % 2) == 0) // It won't be aligned with the terminating byte smb_message_put16(req, 0); smb_message_put32(req, 1); // Level 1 ? smb_message_put32(req, 1); // Ctr ? smb_message_put32(req, 0x00020004); // Referent ID ? smb_message_put64(req, 0); // Count/Null Pointer to NetShareInfo1 smb_message_put32(req, 0xffffffff); // Max Buffer (0xffffffff required by smbX) smb_message_put32(req, 0x00020008); // Referent ID ? smb_message_put32(req, 0); // Resume ? // fill trans pkt at the end since we know the size at the end SMB_MSG_INIT_PKT(trans); trans.wct = 16; trans.max_data_count = 4280; trans.setup_count = 2; trans.pipe_function = 0x26; // TransactNmPipe; trans.fid = SMB_FD_FID(srvscv_fd); trans.bct = req->cursor - sizeof(smb_trans_req); trans.data_count = trans.bct - 17; // 17 -> padding + \PIPE\ + padding trans.total_data_count = trans.data_count; trans.data_offset = 84; trans.param_offset = 84; // but insert it at the begining SMB_MSG_INSERT_PKT(req, 0, trans); req->packet->payload[frag_len_cursor] = trans.data_count; // (data_count SHOULD stay < 256) // Let's send this ugly pile of shit over the network ! res = smb_session_send_msg(s, req); smb_message_destroy(req); if (!res) { ret = DSM_ERROR_NETWORK; goto error; } // Is the server throwing pile of shit back at me ? res = smb_session_recv_msg(s, &resp); if (!res && (uint32_t)resp.packet->payload[resp.payload_size - 4]) { BDSM_dbg("NetShareEnumAll call failed.\n"); ret = DSM_ERROR_NETWORK; goto error; } //// Phase 3 // We parse the list of Share (finally !) and build function response count = smb_share_parse_enum(&resp, list); if (count == -1) { ret = DSM_ERROR_GENERIC; goto error; } if (pcount != NULL) *pcount = count; ret = DSM_SUCCESS; error: // Close the pipe smb_fclose(s, srvscv_fd); return ret; } ret = DSM_ERROR_GENERIC; }
smb_file *smb_fstat(smb_session *s, smb_tid tid, const char *path) { smb_message *msg, reply; smb_trans2_req tr2; smb_trans2_resp *tr2_resp; smb_tr2_query query; smb_tr2_path_info *info; smb_file *file; size_t utf_path_len, msg_len; char *utf_path; int res, padding = 0; assert(s != NULL && path != NULL); utf_path_len = smb_to_utf16(path, strlen(path) + 1, &utf_path); if (utf_path_len == 0) return 0; msg_len = sizeof(smb_trans2_req) + sizeof(smb_tr2_query); msg_len += utf_path_len; if (msg_len %4) padding = 4 - msg_len % 4; msg = smb_message_new(SMB_CMD_TRANS2); if (!msg) { free(utf_path); return 0; } msg->packet->header.tid = tid; SMB_MSG_INIT_PKT(tr2); tr2.wct = 15; tr2.total_param_count = utf_path_len + sizeof(smb_tr2_query); tr2.param_count = tr2.total_param_count; tr2.max_param_count = 2; // ?? Why not the same or 12 ? tr2.max_data_count = 0xffff; tr2.param_offset = 68; // Offset of find_first_params in packet; tr2.data_count = 0; tr2.data_offset = 96; // Offset of pattern in packet tr2.setup_count = 1; tr2.cmd = SMB_TR2_QUERY_PATH; tr2.bct = sizeof(smb_tr2_query) + utf_path_len + padding; SMB_MSG_PUT_PKT(msg, tr2); SMB_MSG_INIT_PKT(query); query.interest = SMB_FIND2_QUERY_FILE_ALL_INFO; SMB_MSG_PUT_PKT(msg, query); smb_message_append(msg, utf_path, utf_path_len); free(utf_path); // Adds padding at the end if necessary. while (padding--) smb_message_put8(msg, 0); res = smb_session_send_msg(s, msg); smb_message_destroy(msg); if (!res) { BDSM_dbg("Unable to query pattern: %s\n", path); return NULL; } if (!smb_session_recv_msg(s, &reply) || reply.packet->header.status != NT_STATUS_SUCCESS) { BDSM_dbg("Unable to recv msg or failure for %s\n", path); return NULL; } tr2_resp = (smb_trans2_resp *)reply.packet->payload; info = (smb_tr2_path_info *)(tr2_resp->payload + 4); //+4 is padding file = calloc(1, sizeof(smb_file)); if (!file) return NULL; file->name_len = smb_from_utf16((const char *)info->name, info->name_len, &file->name); file->name[info->name_len / 2] = 0; file->created = info->created; file->accessed = info->accessed; file->written = info->written; file->changed = info->changed; file->alloc_size = info->alloc_size; file->size = info->size; file->attr = info->attr; file->is_dir = info->is_dir; return file; }
smb_file *smb_find(smb_session *s, smb_tid tid, const char *pattern) { smb_file *files = NULL; smb_message *msg; smb_trans2_resp *tr2_resp; smb_tr2_findfirst2_params *findfirst2_params; smb_tr2_findnext2_params *findnext2_params; bool end_of_search; uint16_t sid; uint16_t resume_key; uint16_t error_offset; assert(s != NULL && pattern != NULL); // Send FIND_FIRST request msg = smb_trans2_find_first(s,tid,pattern); if (msg) { smb_find_first_parse(msg,&files); if (files) { // Check if we shall send a FIND_NEXT request tr2_resp = (smb_trans2_resp *)msg->packet->payload; findfirst2_params = (smb_tr2_findfirst2_params *)tr2_resp->payload; sid = findfirst2_params->id; end_of_search = findfirst2_params->eos; resume_key = findfirst2_params->last_name_offset; error_offset = findfirst2_params->ea_error_offset; smb_message_destroy(msg); // Send FIND_NEXT queries until the find is finished // or until an error occurs while ((!end_of_search) && (error_offset == 0)) { msg = smb_trans2_find_next(s, tid, resume_key, sid, pattern); if (msg) { // Update info for next FIND_NEXT query tr2_resp = (smb_trans2_resp *)msg->packet->payload; findnext2_params = (smb_tr2_findnext2_params *)tr2_resp->payload; end_of_search = findnext2_params->eos; resume_key = findnext2_params->last_name_offset; error_offset = findnext2_params->ea_error_offset; // parse the result for files smb_find_next_parse(msg, &files); smb_message_destroy(msg); if (!files) { BDSM_dbg("Error during FIND_NEXT answer parsing\n"); end_of_search = true; } } else { BDSM_dbg("Error during FIND_NEXT request\n"); smb_stat_list_destroy(files); return NULL; } } } else { BDSM_dbg("Error during FIND_FIRST answer parsing\n"); smb_message_destroy(msg); } } else { BDSM_dbg("Error during FIND_FIRST request\n"); smb_stat_list_destroy(files); smb_message_destroy(msg); return NULL; } return files; }
static smb_message *smb_trans2_find_next (smb_session *s, smb_tid tid, uint16_t resume_key, uint16_t sid, const char *pattern) { smb_message *msg_find_next2 = NULL; smb_trans2_req tr2_find_next2; smb_tr2_findnext2 find_next2; size_t utf_pattern_len, tr2_bct, tr2_param_count; char *utf_pattern; int res; unsigned int padding = 0; assert(s != NULL && pattern != NULL); utf_pattern_len = smb_to_utf16(pattern, strlen(pattern) + 1, &utf_pattern); if (utf_pattern_len == 0) return NULL; tr2_bct = sizeof(smb_tr2_findnext2) + utf_pattern_len; tr2_param_count = tr2_bct; tr2_bct += 3; // Adds padding at the end if necessary. while ((tr2_bct % 4) != 3) { padding++; tr2_bct++; } msg_find_next2 = smb_message_new(SMB_CMD_TRANS2); if (!msg_find_next2) { free(utf_pattern); return NULL; } msg_find_next2->packet->header.tid = tid; SMB_MSG_INIT_PKT(tr2_find_next2); tr2_find_next2.wct = 0x0f; tr2_find_next2.total_param_count = tr2_param_count; tr2_find_next2.total_data_count = 0x0000; tr2_find_next2.max_param_count = 10; // ?? Why not the same or 12 ? tr2_find_next2.max_data_count = 0xffff; //max_setup_count //reserved //flags //timeout //reserve2 tr2_find_next2.param_count = tr2_param_count; tr2_find_next2.param_offset = 68; // Offset of find_next_params in packet; tr2_find_next2.data_count = 0; tr2_find_next2.data_offset = 88; // Offset of pattern in packet tr2_find_next2.setup_count = 1; //reserve3 tr2_find_next2.cmd = SMB_TR2_FIND_NEXT; tr2_find_next2.bct = tr2_bct; //3 == padding SMB_MSG_PUT_PKT(msg_find_next2, tr2_find_next2); SMB_MSG_INIT_PKT(find_next2); find_next2.sid = sid; find_next2.count = 255; find_next2.interest = SMB_FIND2_INTEREST_BOTH_DIRECTORY_INFO; find_next2.flags = SMB_FIND2_FLAG_CLOSE_EOS|SMB_FIND2_FLAG_CONTINUE; find_next2.resume_key = resume_key; SMB_MSG_PUT_PKT(msg_find_next2, find_next2); smb_message_append(msg_find_next2, utf_pattern, utf_pattern_len); while (padding--) smb_message_put8(msg_find_next2, 0); res = smb_session_send_msg(s, msg_find_next2); smb_message_destroy(msg_find_next2); free(utf_pattern); if (!res) { BDSM_dbg("Unable to query pattern: %s\n", pattern); return NULL; } msg_find_next2 = smb_tr2_recv(s); return msg_find_next2; }
static smb_message *smb_trans2_find_first (smb_session *s, smb_tid tid, const char *pattern) { smb_message *msg; smb_trans2_req tr2; smb_tr2_findfirst2 find; size_t utf_pattern_len, tr2_bct, tr2_param_count; char *utf_pattern; int res; unsigned int padding = 0; assert(s != NULL && pattern != NULL); utf_pattern_len = smb_to_utf16(pattern, strlen(pattern) + 1, &utf_pattern); if (utf_pattern_len == 0) return NULL; tr2_bct = sizeof(smb_tr2_findfirst2) + utf_pattern_len; tr2_param_count = tr2_bct; tr2_bct += 3; // Adds padding at the end if necessary. while ((tr2_bct % 4) != 3) { padding++; tr2_bct++; } msg = smb_message_new(SMB_CMD_TRANS2); if (!msg) { free(utf_pattern); return NULL; } msg->packet->header.tid = tid; SMB_MSG_INIT_PKT(tr2); tr2.wct = 15; tr2.max_param_count = 10; // ?? Why not the same or 12 ? tr2.max_data_count = 0xffff;; tr2.param_offset = 68; // Offset of find_first_params in packet; tr2.data_count = 0; tr2.data_offset = 88; // Offset of pattern in packet tr2.setup_count = 1; tr2.cmd = SMB_TR2_FIND_FIRST; tr2.total_param_count = tr2_param_count; tr2.param_count = tr2_param_count; tr2.bct = tr2_bct; //3 == padding SMB_MSG_PUT_PKT(msg, tr2); SMB_MSG_INIT_PKT(find); find.attrs = SMB_FIND2_ATTR_DEFAULT; find.count = 1366; // ?? find.flags = SMB_FIND2_FLAG_CLOSE_EOS | SMB_FIND2_FLAG_RESUME; find.interest = SMB_FIND2_INTEREST_BOTH_DIRECTORY_INFO; SMB_MSG_PUT_PKT(msg, find); smb_message_append(msg, utf_pattern, utf_pattern_len); while (padding--) smb_message_put8(msg, 0); res = smb_session_send_msg(s, msg); smb_message_destroy(msg); free(utf_pattern); if (!res) { BDSM_dbg("Unable to query pattern: %s\n", pattern); return NULL; } msg = smb_tr2_recv(s); return msg; }