Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/libnet/libnet_join_offline.c
Line
Count
Source
1
/*
2
 *  Unix SMB/CIFS implementation.
3
 *  libnet Join offline support
4
 *  Copyright (C) Guenther Deschner 2021
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 "librpc/gen_ndr/ndr_libnet_join.h"
22
#include "../librpc/gen_ndr/ndr_ODJ.h"
23
#include "libnet/libnet_join_offline.h"
24
#include "libcli/security/dom_sid.h"
25
#include "rpc_client/util_netlogon.h"
26
27
static WERROR libnet_odj_compose_ODJ_WIN7BLOB(TALLOC_CTX *mem_ctx,
28
                const struct libnet_JoinCtx *r,
29
                struct ODJ_WIN7BLOB *b)
30
0
{
31
0
  char *samaccount;
32
0
  uint32_t len;
33
0
  struct ODJ_POLICY_DNS_DOMAIN_INFO i = {
34
0
    .Sid = NULL,
35
0
  };
36
37
0
  ZERO_STRUCTP(b);
38
39
0
  b->lpDomain = talloc_strdup(mem_ctx, r->out.dns_domain_name);
40
0
  if (b->lpDomain == NULL) {
41
0
    return WERR_NOT_ENOUGH_MEMORY;
42
0
  }
43
44
0
  samaccount = talloc_strdup(mem_ctx, r->out.account_name);
45
0
  if (samaccount == NULL) {
46
0
    return WERR_NOT_ENOUGH_MEMORY;
47
0
  }
48
0
  len = strlen(samaccount);
49
0
  if (samaccount[len-1] == '$') {
50
0
    samaccount[len-1] = '\0';
51
0
  }
52
0
  b->lpMachineName = samaccount;
53
54
0
  b->lpMachinePassword = talloc_strdup(mem_ctx, r->in.machine_password);
55
0
  if (b->lpMachinePassword == NULL) {
56
0
    return WERR_NOT_ENOUGH_MEMORY;
57
0
  }
58
59
  /* fill up ODJ_POLICY_DNS_DOMAIN_INFO */
60
61
0
  i.Name.string = talloc_strdup(mem_ctx, r->out.netbios_domain_name);
62
0
  if (i.Name.string == NULL) {
63
0
    return WERR_NOT_ENOUGH_MEMORY;
64
0
  }
65
66
0
  i.DnsDomainName.string = talloc_strdup(mem_ctx, r->out.dns_domain_name);
67
0
  if (i.DnsDomainName.string == NULL) {
68
0
    return WERR_NOT_ENOUGH_MEMORY;
69
0
  }
70
71
0
  i.DnsForestName.string = talloc_strdup(mem_ctx, r->out.forest_name);
72
0
  if (i.DnsForestName.string == NULL) {
73
0
    return WERR_NOT_ENOUGH_MEMORY;
74
0
  }
75
76
0
  i.DomainGuid = r->out.domain_guid;
77
0
  i.Sid = dom_sid_dup(mem_ctx, r->out.domain_sid);
78
0
  if (i.Sid == NULL) {
79
0
    return WERR_NOT_ENOUGH_MEMORY;
80
0
  }
81
82
0
  b->DnsDomainInfo = i;
83
84
0
  if (r->out.dcinfo) {
85
0
    struct netr_DsRGetDCNameInfo *p;
86
87
0
    p = talloc_steal(mem_ctx, r->out.dcinfo);
88
0
    if (p == NULL) {
89
0
      return WERR_NOT_ENOUGH_MEMORY;
90
0
    }
91
92
0
    b->DcInfo = *p;
93
0
  }
94
95
  /*
96
   * According to
97
   * https://docs.microsoft.com/en-us/windows/win32/netmgmt/odj-odj_win7blob
98
   * it should be 0 but Windows 2019 always sets 6 - gd.
99
   */
100
0
  b->Options = 6;
101
102
0
  return WERR_OK;
103
0
}
104
105
static WERROR libnet_odj_compose_OP_JOINPROV2_PART(TALLOC_CTX *mem_ctx,
106
               const struct libnet_JoinCtx *r,
107
               struct OP_JOINPROV2_PART **p)
108
0
{
109
0
  struct OP_JOINPROV2_PART *b;
110
111
0
  b = talloc_zero(mem_ctx, struct OP_JOINPROV2_PART);
112
0
  if (b == NULL) {
113
0
    return WERR_NOT_ENOUGH_MEMORY;
114
0
  }
115
116
  /* TODO */
117
118
0
  *p = b;
119
120
0
  return WERR_INVALID_LEVEL;
121
0
}
122
123
static WERROR libnet_odj_compose_OP_JOINPROV3_PART(TALLOC_CTX *mem_ctx,
124
               const struct libnet_JoinCtx *r,
125
               struct OP_JOINPROV3_PART **p)
126
0
{
127
0
  struct OP_JOINPROV3_PART *b;
128
0
  struct dom_sid *sid;
129
130
0
  b = talloc_zero(mem_ctx, struct OP_JOINPROV3_PART);
131
0
  if (b == NULL) {
132
0
    return WERR_NOT_ENOUGH_MEMORY;
133
0
  }
134
135
0
  b->Rid = r->out.account_rid;
136
0
  sid = dom_sid_add_rid(mem_ctx, r->out.domain_sid, r->out.account_rid);
137
0
  if (sid == NULL) {
138
0
    return WERR_NOT_ENOUGH_MEMORY;
139
0
  }
140
141
0
  b->lpSid = dom_sid_string(mem_ctx, sid);
142
0
  if (b->lpSid == NULL) {
143
0
    return WERR_NOT_ENOUGH_MEMORY;
144
0
  }
145
146
0
  *p = b;
147
148
0
  return WERR_OK;
149
0
}
150
151
static WERROR libnet_odj_compose_OP_PACKAGE_PART(TALLOC_CTX *mem_ctx,
152
             const struct libnet_JoinCtx *r,
153
             const struct ODJ_WIN7BLOB *win7,
154
             const char *join_provider_guid,
155
             uint32_t flags,
156
             struct OP_PACKAGE_PART *p)
157
0
{
158
0
  struct GUID guid;
159
0
  uint32_t level;
160
0
  WERROR werr;
161
162
0
  if (!NT_STATUS_IS_OK(GUID_from_string(join_provider_guid, &guid))) {
163
0
    return WERR_NOT_ENOUGH_MEMORY;
164
0
  }
165
166
0
  level = odj_switch_level_from_guid(&guid);
167
168
0
  p->PartType = guid;
169
0
  p->ulFlags  = flags;
170
0
  p->part_len = 0; /* autogenerated */
171
0
  p->Part = talloc_zero(mem_ctx, union OP_PACKAGE_PART_u);
172
0
  if (p->Part == NULL) {
173
0
    return WERR_NOT_ENOUGH_MEMORY;
174
0
  }
175
176
0
  switch (level) {
177
0
    case 1: /* ODJ_GUID_JOIN_PROVIDER */
178
0
      if (win7 == NULL) {
179
0
        return WERR_INVALID_PARAMETER;
180
0
      }
181
0
      p->Part->win7blob = *win7;
182
0
      break;
183
0
    case 2: /* ODJ_GUID_JOIN_PROVIDER2 */
184
0
      werr = libnet_odj_compose_OP_JOINPROV2_PART(mem_ctx, r,
185
0
          &p->Part->join_prov2.p);
186
0
      if (!W_ERROR_IS_OK(werr)) {
187
0
        return werr;
188
0
      }
189
0
      break;
190
0
    case 3: /* ODJ_GUID_JOIN_PROVIDER3 */
191
0
      werr = libnet_odj_compose_OP_JOINPROV3_PART(mem_ctx, r,
192
0
          &p->Part->join_prov3.p);
193
0
      if (!W_ERROR_IS_OK(werr)) {
194
0
        return werr;
195
0
      }
196
0
      break;
197
0
    default:
198
0
      return WERR_INVALID_LEVEL;
199
0
  }
200
201
0
  return WERR_OK;
202
0
}
203
204
static WERROR libnet_odj_compose_OP_PACKAGE_PART_COLLECTION(TALLOC_CTX *mem_ctx,
205
                  const struct libnet_JoinCtx *r,
206
                  const struct ODJ_WIN7BLOB *win7,
207
                  struct OP_PACKAGE_PART_COLLECTION **pp)
208
0
{
209
0
  WERROR werr;
210
0
  struct OP_PACKAGE_PART_COLLECTION *p;
211
212
0
  p = talloc_zero(mem_ctx, struct OP_PACKAGE_PART_COLLECTION);
213
0
  if (p == NULL) {
214
0
    return WERR_NOT_ENOUGH_MEMORY;
215
0
  }
216
217
0
  p->cParts = 2;
218
0
  p->pParts = talloc_zero_array(p, struct OP_PACKAGE_PART, p->cParts);
219
0
  if (p->pParts == NULL) {
220
0
    talloc_free(p);
221
0
    return WERR_NOT_ENOUGH_MEMORY;
222
0
  }
223
224
0
  werr = libnet_odj_compose_OP_PACKAGE_PART(p, r, win7,
225
0
              ODJ_GUID_JOIN_PROVIDER,
226
0
              OPSPI_PACKAGE_PART_ESSENTIAL,
227
0
              &p->pParts[0]);
228
0
  if (!W_ERROR_IS_OK(werr)) {
229
0
    talloc_free(p);
230
0
    return werr;
231
0
  }
232
233
0
  werr = libnet_odj_compose_OP_PACKAGE_PART(p, r, NULL,
234
0
              ODJ_GUID_JOIN_PROVIDER3,
235
0
              0,
236
0
              &p->pParts[1]);
237
0
  if (!W_ERROR_IS_OK(werr)) {
238
0
    talloc_free(p);
239
0
    return werr;
240
0
  }
241
242
0
  *pp = p;
243
244
0
  return WERR_OK;
245
0
}
246
247
static WERROR libnet_odj_compose_OP_PACKAGE(TALLOC_CTX *mem_ctx,
248
              const struct libnet_JoinCtx *r,
249
              const struct ODJ_WIN7BLOB *win7,
250
              struct OP_PACKAGE **pp)
251
0
{
252
0
  WERROR werr;
253
0
  struct OP_PACKAGE_PART_COLLECTION *c;
254
0
  struct OP_PACKAGE *p;
255
256
0
  p = talloc_zero(mem_ctx, struct OP_PACKAGE);
257
0
  if (p == NULL) {
258
0
    return WERR_NOT_ENOUGH_MEMORY;
259
0
  }
260
261
0
  werr = libnet_odj_compose_OP_PACKAGE_PART_COLLECTION(p, r, win7, &c);
262
0
  if (!W_ERROR_IS_OK(werr)) {
263
0
    talloc_free(p);
264
0
    return werr;
265
0
  }
266
267
0
  p->EncryptionType = GUID_zero();
268
269
0
  p->WrappedPartCollection.cbBlob = 0; /* autogenerated */
270
0
  p->WrappedPartCollection.w = talloc_zero(p,
271
0
      struct OP_PACKAGE_PART_COLLECTION_serialized_ptr);
272
0
  if (p->WrappedPartCollection.w == NULL) {
273
0
    talloc_free(p);
274
0
    return WERR_NOT_ENOUGH_MEMORY;
275
0
  }
276
277
0
  p->WrappedPartCollection.w->s.p = c;
278
279
0
  *pp = p;
280
281
0
  return WERR_OK;
282
0
}
283
284
WERROR libnet_odj_compose_ODJ_PROVISION_DATA(TALLOC_CTX *mem_ctx,
285
               const struct libnet_JoinCtx *r,
286
               struct ODJ_PROVISION_DATA **b_p)
287
0
{
288
0
  WERROR werr;
289
0
  struct ODJ_PROVISION_DATA *b;
290
0
  struct ODJ_WIN7BLOB win7;
291
0
  struct OP_PACKAGE *package;
292
293
0
  b = talloc_zero(mem_ctx, struct ODJ_PROVISION_DATA);
294
0
  if (b == NULL) {
295
0
    return WERR_NOT_ENOUGH_MEMORY;
296
0
  }
297
298
0
  b->ulVersion  = 1;
299
0
  b->ulcBlobs = 2;
300
0
  b->pBlobs = talloc_zero_array(b, struct ODJ_BLOB, b->ulcBlobs);
301
0
  if (b->pBlobs == NULL) {
302
0
    talloc_free(b);
303
0
    return WERR_NOT_ENOUGH_MEMORY;
304
0
  }
305
306
0
  werr = libnet_odj_compose_ODJ_WIN7BLOB(b, r, &win7);
307
0
  if (!W_ERROR_IS_OK(werr)) {
308
0
    talloc_free(b);
309
0
    return werr;
310
0
  }
311
312
0
  werr = libnet_odj_compose_OP_PACKAGE(b, r, &win7, &package);
313
0
  if (!W_ERROR_IS_OK(werr)) {
314
0
    talloc_free(b);
315
0
    return werr;
316
0
  }
317
318
0
  b->pBlobs[0].ulODJFormat = ODJ_WIN7_FORMAT;
319
0
  b->pBlobs[0].cbBlob = 0; /* autogenerated */
320
0
  b->pBlobs[0].pBlob = talloc_zero(b, union ODJ_BLOB_u);
321
0
  if (b->pBlobs[0].pBlob == NULL) {
322
0
    talloc_free(b);
323
0
    return WERR_NOT_ENOUGH_MEMORY;
324
0
  }
325
0
  b->pBlobs[0].pBlob->odj_win7blob = win7;
326
327
0
  b->pBlobs[1].ulODJFormat = ODJ_WIN8_FORMAT;
328
0
  b->pBlobs[1].cbBlob = 0; /* autogenerated */
329
0
  b->pBlobs[1].pBlob = talloc_zero(b, union ODJ_BLOB_u);
330
0
  if (b->pBlobs[1].pBlob == NULL) {
331
0
    talloc_free(b);
332
0
    return WERR_NOT_ENOUGH_MEMORY;
333
0
  }
334
0
  b->pBlobs[1].pBlob->op_package.p = package;
335
336
0
  *b_p = b;
337
338
0
  return WERR_OK;
339
0
}
340
341
WERROR libnet_odj_find_win7blob(const struct ODJ_PROVISION_DATA *r,
342
        struct ODJ_WIN7BLOB *win7blob)
343
0
{
344
0
  int i;
345
346
0
  if (r == NULL) {
347
0
    return WERR_INVALID_PARAMETER;
348
0
  }
349
350
0
  for (i = 0; i < r->ulcBlobs; i++) {
351
352
0
    struct ODJ_BLOB b = r->pBlobs[i];
353
354
0
    switch (b.ulODJFormat) {
355
0
    case ODJ_WIN7_FORMAT:
356
0
      *win7blob = b.pBlob->odj_win7blob;
357
0
      return WERR_OK;
358
359
0
    case ODJ_WIN8_FORMAT: {
360
0
      NTSTATUS status;
361
0
      struct OP_PACKAGE_PART_COLLECTION *col;
362
0
      struct GUID guid;
363
0
      int k;
364
365
0
      if (b.pBlob->op_package.p->WrappedPartCollection.w == NULL) {
366
0
        return WERR_BAD_FORMAT;
367
0
      }
368
369
0
      col = b.pBlob->op_package.p->WrappedPartCollection.w->s.p;
370
371
0
      status = GUID_from_string(ODJ_GUID_JOIN_PROVIDER, &guid);
372
0
      if (!NT_STATUS_IS_OK(status)) {
373
0
        return WERR_NOT_ENOUGH_MEMORY;
374
0
      }
375
376
0
      for (k = 0; k < col->cParts; k++) {
377
0
        if (GUID_equal(&guid, &col->pParts[k].PartType)) {
378
0
          *win7blob = col->pParts[k].Part->win7blob;
379
0
          return WERR_OK;
380
0
        }
381
0
      }
382
0
      break;
383
0
    }
384
0
    default:
385
0
      return WERR_BAD_FORMAT;
386
0
    }
387
0
  }
388
389
0
  return WERR_BAD_FORMAT;
390
0
}
391
392
393
WERROR libnet_odj_find_joinprov3(const struct ODJ_PROVISION_DATA *r,
394
         struct OP_JOINPROV3_PART *joinprov3)
395
0
{
396
0
  int i;
397
398
0
  if (r == NULL) {
399
0
    return WERR_INVALID_PARAMETER;
400
0
  }
401
402
0
  for (i = 0; i < r->ulcBlobs; i++) {
403
404
0
    struct ODJ_BLOB b = r->pBlobs[i];
405
406
0
    switch (b.ulODJFormat) {
407
0
    case ODJ_WIN7_FORMAT:
408
0
      continue;
409
410
0
    case ODJ_WIN8_FORMAT: {
411
0
      NTSTATUS status;
412
0
      struct OP_PACKAGE_PART_COLLECTION *col;
413
0
      struct GUID guid;
414
0
      int k;
415
416
0
      if (b.pBlob->op_package.p->WrappedPartCollection.w == NULL) {
417
0
        return WERR_BAD_FORMAT;
418
0
      }
419
420
0
      col = b.pBlob->op_package.p->WrappedPartCollection.w->s.p;
421
422
0
      status = GUID_from_string(ODJ_GUID_JOIN_PROVIDER3, &guid);
423
0
      if (!NT_STATUS_IS_OK(status)) {
424
0
        return WERR_NOT_ENOUGH_MEMORY;
425
0
      }
426
427
0
      for (k = 0; k < col->cParts; k++) {
428
0
        if (GUID_equal(&guid, &col->pParts[k].PartType)) {
429
0
          *joinprov3 = *col->pParts[k].Part->join_prov3.p;
430
0
          return WERR_OK;
431
0
        }
432
0
      }
433
0
      break;
434
0
    }
435
0
    default:
436
0
      return WERR_BAD_FORMAT;
437
0
    }
438
0
  }
439
440
0
  return WERR_BAD_FORMAT;
441
0
}