Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/auth/msrpc_parse.c
Line
Count
Source
1
/* 
2
   Unix SMB/CIFS implementation.
3
   simple kerberos5/SPNEGO routines
4
   Copyright (C) Andrew Tridgell 2001
5
   Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
6
   Copyright (C) Andrew Bartlett 2002-2003
7
   
8
   This program is free software; you can redistribute it and/or modify
9
   it under the terms of the GNU General Public License as published by
10
   the Free Software Foundation; either version 3 of the License, or
11
   (at your option) any later version.
12
   
13
   This program is distributed in the hope that it will be useful,
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
   GNU General Public License for more details.
17
   
18
   You should have received a copy of the GNU General Public License
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
*/
21
22
#include "includes.h"
23
#include "libcli/auth/msrpc_parse.h"
24
25
/*
26
  this is a tiny msrpc packet generator. I am only using this to
27
  avoid tying this code to a particular variant of our rpc code. This
28
  generator is not general enough for all our rpc needs, its just
29
  enough for the spnego/ntlmssp code
30
31
  format specifiers are:
32
33
  U = unicode string (input is unix string)
34
  a = address (input is char *unix_string)
35
      (1 byte type, 1 byte length, unicode/ASCII string, all inline)
36
  A = ASCII string (input is unix string)
37
  B = data blob (pointer + length)
38
  b = data blob in header (pointer + length)
39
  D
40
  d = word (4 bytes)
41
  C = constant ascii string
42
 */
43
NTSTATUS msrpc_gen(TALLOC_CTX *mem_ctx, 
44
         DATA_BLOB *blob,
45
         const char *format, ...)
46
0
{
47
0
  int i, j;
48
0
  bool ret;
49
0
  va_list ap;
50
0
  char *s;
51
0
  uint8_t *b;
52
0
  int head_size=0, data_size=0;
53
0
  int head_ofs, data_ofs;
54
0
  int *intargs;
55
0
  size_t n;
56
57
0
  DATA_BLOB *pointers;
58
59
0
  pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format));
60
0
  if (!pointers) {
61
0
    return NT_STATUS_NO_MEMORY;
62
0
  }
63
0
  intargs = talloc_array(pointers, int, strlen(format));
64
0
  if (!intargs) {
65
0
    return NT_STATUS_NO_MEMORY;
66
0
  }
67
68
  /* first scan the format to work out the header and body size */
69
0
  va_start(ap, format);
70
0
  for (i=0; format[i]; i++) {
71
0
    switch (format[i]) {
72
0
    case 'U':
73
0
      s = va_arg(ap, char *);
74
0
      head_size += 8;
75
0
      ret = push_ucs2_talloc(
76
0
        pointers,
77
0
        (smb_ucs2_t **)(void *)&pointers[i].data,
78
0
        s, &n);
79
0
      if (!ret) {
80
0
        va_end(ap);
81
0
        return map_nt_error_from_unix_common(errno);
82
0
      }
83
0
      pointers[i].length = n;
84
0
      pointers[i].length -= 2;
85
0
      data_size += pointers[i].length;
86
0
      break;
87
0
    case 'A':
88
0
      s = va_arg(ap, char *);
89
0
      head_size += 8;
90
0
      ret = push_ascii_talloc(
91
0
        pointers, (char **)(void *)&pointers[i].data,
92
0
        s, &n);
93
0
      if (!ret) {
94
0
        va_end(ap);
95
0
        return map_nt_error_from_unix_common(errno);
96
0
      }
97
0
      pointers[i].length = n;
98
0
      pointers[i].length -= 1;
99
0
      data_size += pointers[i].length;
100
0
      break;
101
0
    case 'a':
102
0
      j = va_arg(ap, int);
103
0
      intargs[i] = j;
104
0
      s = va_arg(ap, char *);
105
0
      ret = push_ucs2_talloc(
106
0
        pointers,
107
0
        (smb_ucs2_t **)(void *)&pointers[i].data,
108
0
        s, &n);
109
0
      if (!ret) {
110
0
        va_end(ap);
111
0
        return map_nt_error_from_unix_common(errno);
112
0
      }
113
0
      pointers[i].length = n;
114
0
      pointers[i].length -= 2;
115
0
      data_size += pointers[i].length + 4;
116
0
      break;
117
0
    case 'B':
118
0
      b = va_arg(ap, uint8_t *);
119
0
      head_size += 8;
120
0
      pointers[i].data = b;
121
0
      pointers[i].length = va_arg(ap, int);
122
0
      data_size += pointers[i].length;
123
0
      break;
124
0
    case 'b':
125
0
      b = va_arg(ap, uint8_t *);
126
0
      pointers[i].data = b;
127
0
      pointers[i].length = va_arg(ap, int);
128
0
      head_size += pointers[i].length;
129
0
      break;
130
0
    case 'd':
131
0
      j = va_arg(ap, int);
132
0
      intargs[i] = j;
133
0
      head_size += 4;
134
0
      break;
135
0
    case 'C':
136
0
      s = va_arg(ap, char *);
137
0
      pointers[i].data = (uint8_t *)s;
138
0
      pointers[i].length = strlen(s)+1;
139
0
      head_size += pointers[i].length;
140
0
      break;
141
0
    default:
142
0
      va_end(ap);
143
0
      return NT_STATUS_INVALID_PARAMETER;
144
0
    }
145
0
  }
146
0
  va_end(ap);
147
148
0
  if (head_size + data_size == 0) {
149
0
    return NT_STATUS_INVALID_PARAMETER;
150
0
  }
151
152
  /* allocate the space, then scan the format again to fill in the values */
153
0
  *blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size);
154
0
  if (!blob->data) {
155
0
    return NT_STATUS_NO_MEMORY;
156
0
  }
157
0
  head_ofs = 0;
158
0
  data_ofs = head_size;
159
160
0
  va_start(ap, format);
161
0
  for (i=0; format[i]; i++) {
162
0
    switch (format[i]) {
163
0
    case 'U':
164
0
    case 'A':
165
0
    case 'B':
166
0
      n = pointers[i].length;
167
0
      SSVAL(blob->data, head_ofs, n); head_ofs += 2;
168
0
      SSVAL(blob->data, head_ofs, n); head_ofs += 2;
169
0
      SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
170
0
      if (pointers[i].data && n) /* don't follow null pointers... */
171
0
        memcpy(blob->data+data_ofs, pointers[i].data, n);
172
0
      data_ofs += n;
173
0
      break;
174
0
    case 'a':
175
0
      j = intargs[i];
176
0
      SSVAL(blob->data, data_ofs, j); data_ofs += 2;
177
178
0
      n = pointers[i].length;
179
0
      SSVAL(blob->data, data_ofs, n); data_ofs += 2;
180
0
      memcpy(blob->data+data_ofs, pointers[i].data, n);
181
0
      data_ofs += n;
182
0
      break;
183
0
    case 'd':
184
0
      j = intargs[i];
185
0
      SIVAL(blob->data, head_ofs, j); 
186
0
      head_ofs += 4;
187
0
      break;
188
0
    case 'b':
189
0
      n = pointers[i].length;
190
0
      if (pointers[i].data && n) {
191
        /* don't follow null pointers... */
192
0
        memcpy(blob->data + head_ofs, pointers[i].data, n);
193
0
      }
194
0
      head_ofs += n;
195
0
      break;
196
0
    case 'C':
197
0
      n = pointers[i].length;
198
0
      memcpy(blob->data + head_ofs, pointers[i].data, n);
199
0
      head_ofs += n;
200
0
      break;
201
0
    default:
202
0
      va_end(ap);
203
0
      return NT_STATUS_INVALID_PARAMETER;
204
0
    }
205
0
  }
206
0
  va_end(ap);
207
  
208
0
  talloc_free(pointers);
209
210
0
  return NT_STATUS_OK;
211
0
}
212
213
214
/* a helpful macro to avoid running over the end of our blob */
215
0
#define NEED_DATA(amount) \
216
0
if ((head_ofs + amount) > blob->length) { \
217
0
        va_end(ap); \
218
0
        return false; \
219
0
}
220
221
/**
222
  this is a tiny msrpc packet parser. This is the partner of msrpc_gen
223
224
  format specifiers are:
225
226
  U = unicode string (output is unix string)
227
  A = ascii string
228
  B = data blob
229
  b = data blob in header
230
  d = word (4 bytes)
231
  C = constant ascii string
232
 */
233
234
bool msrpc_parse(TALLOC_CTX *mem_ctx, 
235
     const DATA_BLOB *blob,
236
     const char *format, ...)
237
0
{
238
0
  int i;
239
0
  va_list ap;
240
0
  char **ps, *s;
241
0
  DATA_BLOB *b;
242
0
  size_t head_ofs = 0;
243
0
  uint16_t len1, len2;
244
0
  uint32_t ptr;
245
0
  uint32_t *v;
246
0
  bool ret = true;
247
248
0
  va_start(ap, format);
249
0
  for (i=0; format[i]; i++) {
250
0
    switch (format[i]) {
251
0
    case 'U':
252
0
      NEED_DATA(8);
253
0
      len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
254
0
      len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
255
0
      ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
256
257
0
      ps = va_arg(ap, char **);
258
0
      if (len1 == 0 && len2 == 0) {
259
0
        *ps = talloc_strdup(mem_ctx, "");
260
0
        if (*ps == NULL) {
261
0
          ret = false;
262
0
          goto cleanup;
263
0
        }
264
0
      } else {
265
0
        size_t pull_len;
266
267
        /* make sure its in the right format - be strict */
268
0
        if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
269
0
          ret = false;
270
0
          goto cleanup;
271
0
        }
272
0
        if (len1 & 1) {
273
          /* if odd length and unicode */
274
0
          ret = false;
275
0
          goto cleanup;
276
0
        }
277
0
        if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
278
0
            blob->data + ptr < blob->data) {
279
0
          ret = false;
280
0
          goto cleanup;
281
0
        }
282
283
0
        if (!convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
284
0
                 blob->data + ptr, len1,
285
0
                 ps, &pull_len)) {
286
0
          ret = false;
287
0
          goto cleanup;
288
0
        }
289
0
      }
290
0
      break;
291
0
    case 'A':
292
0
      NEED_DATA(8);
293
0
      len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
294
0
      len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
295
0
      ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
296
297
0
      ps = (char **)va_arg(ap, char **);
298
      /* make sure its in the right format - be strict */
299
0
      if (len1 == 0 && len2 == 0) {
300
0
        *ps = talloc_strdup(mem_ctx, "");
301
0
        if (*ps == NULL) {
302
0
          ret = false;
303
0
          goto cleanup;
304
0
        }
305
0
      } else {
306
0
        if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
307
0
          ret = false;
308
0
          goto cleanup;
309
0
        }
310
311
0
        if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
312
0
            blob->data + ptr < blob->data) {
313
0
          ret = false;
314
0
          goto cleanup;
315
0
        }
316
317
0
        if (0 < len1) {
318
0
          size_t pull_len;
319
320
0
          if (!convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, 
321
0
                   blob->data + ptr, len1, 
322
0
                   ps, &pull_len)) {
323
0
            ret = false;
324
0
            goto cleanup;
325
0
          }
326
0
        } else {
327
0
          *ps = talloc_strdup(mem_ctx, "");
328
0
          if (*ps == NULL) {
329
0
            ret = false;
330
0
            goto cleanup;
331
0
          }
332
0
        }
333
0
      }
334
0
      break;
335
0
    case 'B':
336
0
      NEED_DATA(8);
337
0
      len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
338
0
      len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
339
0
      ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
340
341
0
      b = (DATA_BLOB *)va_arg(ap, void *);
342
0
      if (len1 == 0 && len2 == 0) {
343
0
        *b = data_blob_talloc(mem_ctx, NULL, 0);
344
0
      } else {
345
        /* make sure its in the right format - be strict */
346
0
        if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
347
0
          ret = false;
348
0
          goto cleanup;
349
0
        }
350
351
0
        if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
352
0
            blob->data + ptr < blob->data) {
353
0
          ret = false;
354
0
          goto cleanup;
355
0
        }
356
357
0
        *b = data_blob_talloc(mem_ctx, blob->data + ptr, len1);
358
0
      }
359
0
      break;
360
0
    case 'b':
361
0
      b = (DATA_BLOB *)va_arg(ap, void *);
362
0
      len1 = va_arg(ap, unsigned int);
363
      /* make sure its in the right format - be strict */
364
0
      NEED_DATA(len1);
365
0
      if (blob->data + head_ofs < (uint8_t *)head_ofs ||
366
0
          blob->data + head_ofs < blob->data) {
367
0
        ret = false;
368
0
        goto cleanup;
369
0
      }
370
371
0
      *b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1);
372
0
      head_ofs += len1;
373
0
      break;
374
0
    case 'd':
375
0
      v = va_arg(ap, uint32_t *);
376
0
      NEED_DATA(4);
377
0
      *v = IVAL(blob->data, head_ofs); head_ofs += 4;
378
0
      break;
379
0
    case 'C':
380
0
      s = va_arg(ap, char *);
381
382
0
      if (blob->data + head_ofs < (uint8_t *)head_ofs ||
383
0
          blob->data + head_ofs < blob->data ||
384
0
          (head_ofs + (strlen(s) + 1)) > blob->length) {
385
0
        ret = false;
386
0
        goto cleanup;
387
0
      }
388
389
0
      if (memcmp(blob->data + head_ofs, s, strlen(s)+1) != 0) {
390
0
        ret = false;
391
0
        goto cleanup;
392
0
      }
393
0
      head_ofs += (strlen(s) + 1);
394
395
0
      break;
396
0
    }
397
0
  }
398
399
0
cleanup:
400
  va_end(ap);
401
0
  return ret;
402
0
}