Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/smb/smb2_negotiate_context.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   Copyright (C) Stefan Metzmacher 2014
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 3 of the License, or
9
   (at your option) any later version.
10
11
   This program is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
#include "includes.h"
21
#include "../libcli/smb/smb_common.h"
22
#include "libcli/smb/smb2_negotiate_context.h"
23
24
static size_t smb2_negotiate_context_padding(uint32_t offset, size_t n)
25
0
{
26
0
  if ((offset & (n-1)) == 0) return 0;
27
0
  return n - (offset & (n-1));
28
0
}
29
30
/*
31
  parse a set of SMB2 create contexts
32
*/
33
NTSTATUS smb2_negotiate_context_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB buffer,
34
              uint16_t expected_count,
35
              struct smb2_negotiate_contexts *contexts)
36
0
{
37
0
  const uint8_t *data = buffer.data;
38
0
  uint32_t remaining = buffer.length;
39
0
  uint16_t idx;
40
41
0
  for (idx = 0; idx < expected_count; idx++) {
42
0
    uint16_t data_length;
43
0
    uint16_t type;
44
0
    NTSTATUS status;
45
0
    size_t pad;
46
0
    uint32_t next_offset;
47
48
0
    if (remaining < 8) {
49
0
      return NT_STATUS_INVALID_PARAMETER;
50
0
    }
51
0
    type        = SVAL(data, 0x00);
52
0
    data_length = SVAL(data, 0x02);
53
#if 0
54
    reserved    = IVAL(data, 0x04);
55
#endif
56
57
0
    next_offset = 0x08 + data_length;
58
0
    if (remaining < next_offset) {
59
0
      return NT_STATUS_INVALID_PARAMETER;
60
0
    }
61
62
0
    status = smb2_negotiate_context_add(
63
0
      mem_ctx, contexts, type, data+0x08, data_length);
64
0
    if (!NT_STATUS_IS_OK(status)) {
65
0
      return status;
66
0
    }
67
68
0
    if (contexts->num_contexts == expected_count) {
69
0
      break;
70
0
    }
71
72
0
    remaining -= next_offset;
73
0
    data += next_offset;
74
75
0
    if (remaining == 0) {
76
0
      break;
77
0
    }
78
79
0
    pad = smb2_negotiate_context_padding(next_offset, 8);
80
0
    if (remaining < pad) {
81
0
      return NT_STATUS_INVALID_PARAMETER;
82
0
    }
83
0
    remaining -= pad;
84
0
    data += pad;
85
0
  }
86
87
0
  if (contexts->num_contexts != expected_count) {
88
0
    return NT_STATUS_INVALID_PARAMETER;
89
0
  }
90
91
0
  return NT_STATUS_OK;
92
0
}
93
94
/*
95
  add a context to a smb2_negotiate attribute context
96
*/
97
static NTSTATUS smb2_negotiate_context_push_one(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
98
            const struct smb2_negotiate_context *context,
99
            bool last)
100
0
{
101
0
  uint32_t ofs = buffer->length;
102
0
  size_t next_offset = 0;
103
0
  size_t next_pad = 0;
104
0
  bool ok;
105
106
0
  if (context->data.length > UINT16_MAX) {
107
0
    return NT_STATUS_INVALID_PARAMETER_MIX;
108
0
  }
109
110
0
  next_offset = 0x08 + context->data.length;
111
0
  if (!last) {
112
0
    next_pad = smb2_negotiate_context_padding(next_offset, 8);
113
0
  }
114
115
0
  ok = data_blob_realloc(mem_ctx, buffer,
116
0
             buffer->length + next_offset + next_pad);
117
0
  if (!ok) {
118
0
    return NT_STATUS_NO_MEMORY;
119
0
  }
120
121
0
  SSVAL(buffer->data, ofs+0x00, context->type);
122
0
  SIVAL(buffer->data, ofs+0x02, context->data.length);
123
0
  SIVAL(buffer->data, ofs+0x04, 0);
124
0
  memcpy(buffer->data+ofs+0x08, context->data.data, context->data.length);
125
0
  if (next_pad > 0) {
126
0
    memset(buffer->data+ofs+next_offset, 0, next_pad);
127
0
  }
128
129
0
  return NT_STATUS_OK;
130
0
}
131
132
/*
133
  create a buffer of a set of create contexts
134
*/
135
NTSTATUS smb2_negotiate_context_push(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
136
             const struct smb2_negotiate_contexts contexts)
137
0
{
138
0
  uint32_t i;
139
0
  NTSTATUS status;
140
141
0
  *buffer = data_blob(NULL, 0);
142
0
  for (i=0; i < contexts.num_contexts; i++) {
143
0
    bool last = false;
144
0
    const struct smb2_negotiate_context *c;
145
146
0
    if ((i + 1) == contexts.num_contexts) {
147
0
      last = true;
148
0
    }
149
150
0
    c = &contexts.contexts[i];
151
0
    status = smb2_negotiate_context_push_one(mem_ctx, buffer, c, last);
152
0
    if (!NT_STATUS_IS_OK(status)) {
153
0
      return status;
154
0
    }
155
0
  }
156
0
  return NT_STATUS_OK;
157
0
}
158
159
NTSTATUS smb2_negotiate_context_add(TALLOC_CTX *mem_ctx,
160
            struct smb2_negotiate_contexts *c,
161
            uint16_t type,
162
            const uint8_t *buf,
163
            size_t buflen)
164
0
{
165
0
  struct smb2_negotiate_context *array;
166
167
0
  array = talloc_realloc(mem_ctx, c->contexts,
168
0
             struct smb2_negotiate_context,
169
0
             c->num_contexts + 1);
170
0
  NT_STATUS_HAVE_NO_MEMORY(array);
171
0
  c->contexts = array;
172
173
0
  c->contexts[c->num_contexts].type = type;
174
175
0
  if (buf != NULL) {
176
0
    c->contexts[c->num_contexts].data = data_blob_talloc(
177
0
      c->contexts, buf, buflen);
178
0
    NT_STATUS_HAVE_NO_MEMORY(c->contexts[c->num_contexts].data.data);
179
0
  } else {
180
0
    c->contexts[c->num_contexts].data = data_blob_null;
181
0
  }
182
183
0
  c->num_contexts += 1;
184
185
0
  return NT_STATUS_OK;
186
0
}
187
188
/*
189
 * return the first blob with the given tag
190
 */
191
struct smb2_negotiate_context *smb2_negotiate_context_find(const struct smb2_negotiate_contexts *c,
192
                 uint16_t type)
193
0
{
194
0
  uint32_t i;
195
196
0
  for (i=0; i < c->num_contexts; i++) {
197
0
    if (c->contexts[i].type ==  type) {
198
0
      return &c->contexts[i];
199
0
    }
200
0
  }
201
202
0
  return NULL;
203
0
}