Coverage Report

Created: 2025-07-23 07:04

/src/samba/auth/credentials/credentials_krb5.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   Handle user credentials (as regards krb5)
5
6
   Copyright (C) Jelmer Vernooij 2005
7
   Copyright (C) Tim Potter 2001
8
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9
10
   This program is free software; you can redistribute it and/or modify
11
   it under the terms of the GNU General Public License as published by
12
   the Free Software Foundation; either version 3 of the License, or
13
   (at your option) any later version.
14
15
   This program is distributed in the hope that it will be useful,
16
   but WITHOUT ANY WARRANTY; without even the implied warranty of
17
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
   GNU General Public License for more details.
19
20
   You should have received a copy of the GNU General Public License
21
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
*/
23
24
#include "includes.h"
25
#include "system/kerberos.h"
26
#include "system/gssapi.h"
27
#include "auth/kerberos/kerberos.h"
28
#include "auth/credentials/credentials.h"
29
#include "auth/credentials/credentials_internal.h"
30
#include "auth/credentials/credentials_krb5.h"
31
#include "auth/kerberos/kerberos_credentials.h"
32
#include "auth/kerberos/kerberos_srv_keytab.h"
33
#include "auth/kerberos/kerberos_util.h"
34
#include "auth/kerberos/pac_utils.h"
35
#include "param/param.h"
36
#include "../libds/common/flags.h"
37
38
#undef DBGC_CLASS
39
0
#define DBGC_CLASS DBGC_AUTH
40
41
#undef strncasecmp
42
43
static void cli_credentials_invalidate_client_gss_creds(
44
          struct cli_credentials *cred,
45
          enum credentials_obtained obtained);
46
47
/* Free a memory ccache */
48
static int free_mccache(struct ccache_container *ccc)
49
0
{
50
0
  if (ccc->ccache != NULL) {
51
0
    krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
52
0
        ccc->ccache);
53
0
    ccc->ccache = NULL;
54
0
  }
55
56
0
  return 0;
57
0
}
58
59
/* Free a disk-based ccache */
60
static int free_dccache(struct ccache_container *ccc)
61
0
{
62
0
  if (ccc->ccache != NULL) {
63
0
    krb5_cc_close(ccc->smb_krb5_context->krb5_context,
64
0
            ccc->ccache);
65
0
    ccc->ccache = NULL;
66
0
  }
67
68
0
  return 0;
69
0
}
70
71
static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
72
           gss_cred_id_t cred,
73
           struct ccache_container *ccc)
74
0
{
75
#ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
76
  krb5_context context = ccc->smb_krb5_context->krb5_context;
77
  krb5_ccache dummy_ccache = NULL;
78
  krb5_creds creds = {0};
79
  krb5_cc_cursor cursor = NULL;
80
  krb5_principal princ = NULL;
81
  krb5_error_code code;
82
  uint32_t maj_stat = GSS_S_FAILURE;
83
84
  /*
85
   * Create a dummy ccache, so we can iterate over the credentials
86
   * and find the default principal for the ccache we want to
87
   * copy. The new ccache needs to be initialized with this
88
   * principal.
89
   */
90
  code = smb_krb5_cc_new_unique_memory(context, NULL, NULL, &dummy_ccache);
91
  if (code != 0) {
92
    *min_stat = code;
93
    return GSS_S_FAILURE;
94
  }
95
96
  /*
97
   * We do not need set a default principal on the temporary dummy
98
   * ccache, as we do consume it at all in this function.
99
   */
100
  maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
101
  if (maj_stat != 0) {
102
    krb5_cc_destroy(context, dummy_ccache);
103
    return maj_stat;
104
  }
105
106
  code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
107
  if (code != 0) {
108
    krb5_cc_destroy(context, dummy_ccache);
109
    *min_stat = EINVAL;
110
    return GSS_S_FAILURE;
111
  }
112
113
  code = krb5_cc_next_cred(context,
114
         dummy_ccache,
115
         &cursor,
116
         &creds);
117
  if (code != 0) {
118
    krb5_cc_destroy(context, dummy_ccache);
119
    *min_stat = EINVAL;
120
    return GSS_S_FAILURE;
121
  }
122
123
  do {
124
    if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
125
      krb5_data *tgs;
126
127
      tgs = krb5_princ_component(context,
128
               creds.server,
129
               0);
130
      if (tgs != NULL && tgs->length >= 1) {
131
        int cmp;
132
133
        cmp = memcmp(tgs->data,
134
               KRB5_TGS_NAME,
135
               tgs->length);
136
        if (cmp == 0 && creds.client != NULL) {
137
          princ = creds.client;
138
          code = KRB5_CC_END;
139
          break;
140
        }
141
      }
142
    }
143
144
    krb5_free_cred_contents(context, &creds);
145
146
    code = krb5_cc_next_cred(context,
147
           dummy_ccache,
148
           &cursor,
149
           &creds);
150
  } while (code == 0);
151
152
  if (code == KRB5_CC_END) {
153
    krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
154
    code = 0;
155
  }
156
  krb5_cc_destroy(context, dummy_ccache);
157
158
  if (code != 0 || princ == NULL) {
159
    krb5_free_cred_contents(context, &creds);
160
    *min_stat = EINVAL;
161
    return GSS_S_FAILURE;
162
  }
163
164
  /*
165
   * Set the default principal for the cache we copy
166
   * into. This is needed to be able that other calls
167
   * can read it with e.g. gss_acquire_cred() or
168
   * krb5_cc_get_principal().
169
   */
170
  code = krb5_cc_initialize(context, ccc->ccache, princ);
171
  if (code != 0) {
172
    krb5_free_cred_contents(context, &creds);
173
    *min_stat = EINVAL;
174
    return GSS_S_FAILURE;
175
  }
176
  krb5_free_cred_contents(context, &creds);
177
178
#endif /* SAMBA4_USES_HEIMDAL */
179
180
0
  return gss_krb5_copy_ccache(min_stat,
181
0
            cred,
182
0
            ccc->ccache);
183
0
}
184
185
_PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
186
             struct loadparm_context *lp_ctx,
187
             struct smb_krb5_context **smb_krb5_context)
188
0
{
189
0
  int ret;
190
0
  if (cred->smb_krb5_context) {
191
0
    *smb_krb5_context = cred->smb_krb5_context;
192
0
    return 0;
193
0
  }
194
195
0
  ret = smb_krb5_init_context(cred, lp_ctx,
196
0
            &cred->smb_krb5_context);
197
0
  if (ret) {
198
0
    cred->smb_krb5_context = NULL;
199
0
    return ret;
200
0
  }
201
0
  *smb_krb5_context = cred->smb_krb5_context;
202
0
  return 0;
203
0
}
204
205
/* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
206
 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
207
 */
208
_PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
209
            struct smb_krb5_context *smb_krb5_context)
210
0
{
211
0
  if (smb_krb5_context == NULL) {
212
0
    talloc_unlink(cred, cred->smb_krb5_context);
213
0
    cred->smb_krb5_context = NULL;
214
0
    return NT_STATUS_OK;
215
0
  }
216
217
0
  if (!talloc_reference(cred, smb_krb5_context)) {
218
0
    return NT_STATUS_NO_MEMORY;
219
0
  }
220
0
  cred->smb_krb5_context = smb_krb5_context;
221
0
  return NT_STATUS_OK;
222
0
}
223
224
static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
225
             struct ccache_container *ccache,
226
             enum credentials_obtained obtained,
227
             const char **error_string)
228
0
{
229
0
  bool ok;
230
0
  char *realm;
231
0
  krb5_principal princ;
232
0
  krb5_error_code ret;
233
0
  char *name;
234
235
0
  if (cred->ccache_obtained > obtained) {
236
0
    return 0;
237
0
  }
238
239
0
  ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
240
0
            ccache->ccache, &princ);
241
242
0
  if (ret) {
243
0
    (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
244
0
              smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
245
0
                       ret, cred));
246
0
    return ret;
247
0
  }
248
249
0
  ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
250
0
  if (ret) {
251
0
    (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
252
0
              smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
253
0
                       ret, cred));
254
0
    krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
255
0
    return ret;
256
0
  }
257
258
0
  ok = cli_credentials_set_principal(cred, name, obtained);
259
0
  krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
260
0
  if (!ok) {
261
0
    krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
262
0
    return ENOMEM;
263
0
  }
264
265
0
  realm = smb_krb5_principal_get_realm(
266
0
    cred, ccache->smb_krb5_context->krb5_context, princ);
267
0
  krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
268
0
  if (realm == NULL) {
269
0
    return ENOMEM;
270
0
  }
271
0
  ok = cli_credentials_set_realm(cred, realm, obtained);
272
0
  TALLOC_FREE(realm);
273
0
  if (!ok) {
274
0
    return ENOMEM;
275
0
  }
276
277
  /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
278
0
  cred->ccache_obtained = obtained;
279
280
0
  return 0;
281
0
}
282
283
_PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
284
          struct loadparm_context *lp_ctx,
285
          const char *name,
286
          enum credentials_obtained obtained,
287
          const char **error_string)
288
0
{
289
0
  krb5_error_code ret;
290
0
  krb5_principal princ;
291
0
  struct ccache_container *ccc;
292
0
  if (cred->ccache_obtained > obtained) {
293
0
    return 0;
294
0
  }
295
296
0
  ccc = talloc(cred, struct ccache_container);
297
0
  if (!ccc) {
298
0
    (*error_string) = error_message(ENOMEM);
299
0
    return ENOMEM;
300
0
  }
301
302
0
  ret = cli_credentials_get_krb5_context(cred, lp_ctx,
303
0
                 &ccc->smb_krb5_context);
304
0
  if (ret) {
305
0
    (*error_string) = error_message(ret);
306
0
    talloc_free(ccc);
307
0
    return ret;
308
0
  }
309
0
  if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
310
0
    talloc_free(ccc);
311
0
    (*error_string) = error_message(ENOMEM);
312
0
    return ENOMEM;
313
0
  }
314
315
0
  if (name) {
316
0
    ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
317
0
    if (ret) {
318
0
      (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
319
0
                name,
320
0
                smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
321
0
                         ret, ccc));
322
0
      talloc_free(ccc);
323
0
      return ret;
324
0
    }
325
0
  } else {
326
    /*
327
     * This is where the caller really wants to use
328
     * the default krb5 ccache.
329
     */
330
0
    ret = smb_force_krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
331
0
    if (ret) {
332
0
      (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
333
0
                smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
334
0
                         ret, ccc));
335
0
      talloc_free(ccc);
336
0
      return ret;
337
0
    }
338
0
  }
339
340
0
  talloc_set_destructor(ccc, free_dccache);
341
342
0
  ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
343
344
0
  if (ret == 0) {
345
0
    krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
346
0
    ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
347
348
0
    if (ret) {
349
0
      (*error_string) = error_message(ret);
350
0
      TALLOC_FREE(ccc);
351
0
      return ret;
352
0
    }
353
0
  }
354
355
0
  cred->ccache = ccc;
356
0
  cred->ccache_obtained = obtained;
357
358
0
  cli_credentials_invalidate_client_gss_creds(
359
0
    cred, cred->ccache_obtained);
360
361
0
  return 0;
362
0
}
363
364
#ifndef SAMBA4_USES_HEIMDAL
365
/*
366
 * This function is a workaround for old MIT Kerberos versions which did not
367
 * implement the krb5_cc_remove_cred function. It creates a temporary
368
 * credentials cache to copy the credentials in the current cache
369
 * except the one we want to remove and then overwrites the contents of the
370
 * current cache with the temporary copy.
371
 */
372
static krb5_error_code krb5_cc_remove_cred_wrap(struct ccache_container *ccc,
373
            krb5_creds *creds)
374
{
375
  krb5_ccache dummy_ccache = NULL;
376
  krb5_creds cached_creds = {0};
377
  krb5_cc_cursor cursor = NULL;
378
  krb5_error_code code;
379
380
  code = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
381
               NULL, NULL,
382
               &dummy_ccache);
383
  if (code != 0) {
384
    DBG_ERR("krb5_cc_resolve failed: %s\n",
385
      smb_get_krb5_error_message(
386
        ccc->smb_krb5_context->krb5_context,
387
        code, ccc));
388
    return code;
389
  }
390
391
  code = krb5_cc_start_seq_get(ccc->smb_krb5_context->krb5_context,
392
             ccc->ccache,
393
             &cursor);
394
  if (code != 0) {
395
    krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
396
        dummy_ccache);
397
398
    DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
399
      smb_get_krb5_error_message(
400
        ccc->smb_krb5_context->krb5_context,
401
        code, ccc));
402
    return code;
403
  }
404
405
  while ((code = krb5_cc_next_cred(ccc->smb_krb5_context->krb5_context,
406
           ccc->ccache,
407
           &cursor,
408
           &cached_creds)) == 0) {
409
    /* If the principal matches skip it and do not copy to the
410
     * temporary cache as this is the one we want to remove */
411
    if (krb5_principal_compare_flags(
412
        ccc->smb_krb5_context->krb5_context,
413
        creds->server,
414
        cached_creds.server,
415
        0)) {
416
      continue;
417
    }
418
419
    code = krb5_cc_store_cred(
420
        ccc->smb_krb5_context->krb5_context,
421
        dummy_ccache,
422
        &cached_creds);
423
    if (code != 0) {
424
      krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
425
          dummy_ccache);
426
      DBG_ERR("krb5_cc_store_cred failed: %s\n",
427
        smb_get_krb5_error_message(
428
          ccc->smb_krb5_context->krb5_context,
429
          code, ccc));
430
      return code;
431
    }
432
  }
433
434
  if (code == KRB5_CC_END) {
435
    krb5_cc_end_seq_get(ccc->smb_krb5_context->krb5_context,
436
            dummy_ccache,
437
            &cursor);
438
    code = 0;
439
  }
440
441
  if (code != 0) {
442
    krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
443
        dummy_ccache);
444
    DBG_ERR("krb5_cc_next_cred failed: %s\n",
445
      smb_get_krb5_error_message(
446
        ccc->smb_krb5_context->krb5_context,
447
        code, ccc));
448
    return code;
449
  }
450
451
  code = krb5_cc_initialize(ccc->smb_krb5_context->krb5_context,
452
          ccc->ccache,
453
          creds->client);
454
  if (code != 0) {
455
    krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
456
        dummy_ccache);
457
    DBG_ERR("krb5_cc_initialize failed: %s\n",
458
      smb_get_krb5_error_message(
459
        ccc->smb_krb5_context->krb5_context,
460
        code, ccc));
461
    return code;
462
  }
463
464
  code = krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
465
          dummy_ccache,
466
          ccc->ccache);
467
  if (code != 0) {
468
    krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
469
        dummy_ccache);
470
    DBG_ERR("krb5_cc_copy_creds failed: %s\n",
471
      smb_get_krb5_error_message(
472
        ccc->smb_krb5_context->krb5_context,
473
        code, ccc));
474
    return code;
475
  }
476
477
  code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
478
             dummy_ccache);
479
  if (code != 0) {
480
    DBG_ERR("krb5_cc_destroy failed: %s\n",
481
      smb_get_krb5_error_message(
482
        ccc->smb_krb5_context->krb5_context,
483
        code, ccc));
484
    return code;
485
  }
486
487
  return code;
488
}
489
#endif
490
491
/*
492
 * Indicate that we failed to log in to this service/host with these
493
 * credentials.  The caller passes an unsigned int which they
494
 * initialise to the number of times they would like to retry.
495
 *
496
 * This method is used to support re-trying with freshly fetched
497
 * credentials in case a server is rebuilt while clients have
498
 * non-expired tickets. When the client code gets a logon failure they
499
 * throw away the existing credentials for the server and retry.
500
 */
501
_PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
502
                const char *principal,
503
                unsigned int *count)
504
0
{
505
0
  struct ccache_container *ccc;
506
0
  krb5_creds creds, creds2;
507
0
  int ret;
508
509
0
  if (principal == NULL) {
510
    /* no way to delete if we don't know the principal */
511
0
    return false;
512
0
  }
513
514
0
  ccc = cred->ccache;
515
0
  if (ccc == NULL) {
516
    /* not a kerberos connection */
517
0
    return false;
518
0
  }
519
520
0
  if (*count > 0) {
521
    /* We have already tried discarding the credentials */
522
0
    return false;
523
0
  }
524
0
  (*count)++;
525
526
0
  ZERO_STRUCT(creds);
527
0
  ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
528
0
  if (ret != 0) {
529
0
    return false;
530
0
  }
531
532
  /* MIT kerberos requires creds.client to match against cached
533
   * credentials */
534
0
  ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
535
0
            ccc->ccache,
536
0
            &creds.client);
537
0
  if (ret != 0) {
538
0
    krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
539
0
          &creds);
540
0
    DBG_ERR("krb5_cc_get_principal failed: %s\n",
541
0
      smb_get_krb5_error_message(
542
0
        ccc->smb_krb5_context->krb5_context,
543
0
        ret, ccc));
544
0
    return false;
545
0
  }
546
547
0
  ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
548
0
  if (ret != 0) {
549
    /* don't retry - we didn't find these credentials to remove */
550
0
    krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
551
0
    return false;
552
0
  }
553
554
0
  ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
555
#ifndef SAMBA4_USES_HEIMDAL
556
  if (ret == KRB5_CC_NOSUPP) {
557
    /* Old MIT kerberos versions did not implement
558
     * krb5_cc_remove_cred */
559
    ret = krb5_cc_remove_cred_wrap(ccc, &creds);
560
  }
561
#endif
562
0
  krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
563
0
  krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
564
0
  if (ret != 0) {
565
    /* don't retry - we didn't find these credentials to
566
     * remove. Note that with the current backend this
567
     * never happens, as it always returns 0 even if the
568
     * creds don't exist, which is why we do a separate
569
     * krb5_cc_retrieve_cred() above.
570
     */
571
0
    DBG_ERR("krb5_cc_remove_cred failed: %s\n",
572
0
      smb_get_krb5_error_message(
573
0
        ccc->smb_krb5_context->krb5_context,
574
0
        ret, ccc));
575
0
    return false;
576
0
  }
577
0
  return true;
578
0
}
579
580
581
static int cli_credentials_new_ccache(struct cli_credentials *cred,
582
              struct loadparm_context *lp_ctx,
583
              char *given_ccache_name,
584
              struct ccache_container **_ccc,
585
              const char **error_string)
586
0
{
587
0
  char *ccache_name = given_ccache_name;
588
0
  bool must_free_cc_name = false;
589
0
  krb5_error_code ret;
590
0
  struct ccache_container *ccc = talloc(cred, struct ccache_container);
591
0
  if (!ccc) {
592
0
    return ENOMEM;
593
0
  }
594
595
0
  ret = cli_credentials_get_krb5_context(cred, lp_ctx,
596
0
                 &ccc->smb_krb5_context);
597
0
  if (ret) {
598
0
    talloc_free(ccc);
599
0
    (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
600
0
              error_message(ret));
601
0
    return ret;
602
0
  }
603
0
  if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
604
0
    talloc_free(ccc);
605
0
    (*error_string) = strerror(ENOMEM);
606
0
    return ENOMEM;
607
0
  }
608
609
0
  if (!ccache_name) {
610
0
    if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
611
0
      ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
612
0
                  (unsigned int)getpid(), ccc);
613
0
      if (ccache_name == NULL) {
614
0
        talloc_free(ccc);
615
0
        (*error_string) = strerror(ENOMEM);
616
0
        return ENOMEM;
617
0
      }
618
0
      must_free_cc_name = true;
619
0
    }
620
0
  }
621
622
0
  if (ccache_name != NULL) {
623
0
    ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
624
0
              &ccc->ccache);
625
0
  } else {
626
0
    ret = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
627
0
                ccc, &ccache_name,
628
0
                &ccc->ccache);
629
0
    must_free_cc_name = true;
630
0
  }
631
0
  if (ret) {
632
0
    (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
633
0
              ccache_name,
634
0
              smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
635
0
                       ret, ccc));
636
0
    talloc_free(ccc);
637
0
    return ret;
638
0
  }
639
640
0
  if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
641
0
    talloc_set_destructor(ccc, free_mccache);
642
0
  } else {
643
0
    talloc_set_destructor(ccc, free_dccache);
644
0
  }
645
646
0
  if (must_free_cc_name) {
647
0
    talloc_free(ccache_name);
648
0
  }
649
650
0
  *_ccc = ccc;
651
652
0
  return 0;
653
0
}
654
655
_PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
656
                struct tevent_context *event_ctx,
657
                struct loadparm_context *lp_ctx,
658
                char *ccache_name,
659
                struct ccache_container **ccc,
660
                const char **error_string)
661
0
{
662
0
  krb5_error_code ret;
663
0
  enum credentials_obtained obtained;
664
665
0
  if (cred->machine_account_pending) {
666
0
    cli_credentials_set_machine_account(cred, lp_ctx);
667
0
  }
668
669
0
  if (cred->ccache_obtained >= cred->ccache_threshold &&
670
0
      cred->ccache_obtained > CRED_UNINITIALISED) {
671
0
    time_t lifetime;
672
0
    enum credentials_obtained pass_obtained =
673
0
      cli_credentials_get_password_obtained(cred);
674
0
    bool kinit_required = false;
675
0
    ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
676
0
                 cred->ccache->ccache, &lifetime);
677
0
    if (ret == KRB5_CC_END || ret == ENOENT) {
678
0
      kinit_required = true;
679
0
    } else if (ret == 0) {
680
0
      if (lifetime == 0) {
681
0
        kinit_required = true;
682
0
      } else if (lifetime < 300) {
683
0
        kinit_required = true;
684
0
      }
685
0
    } else {
686
0
      kinit_required = true;
687
0
    }
688
689
0
    if (!kinit_required) {
690
0
      *ccc = cred->ccache;
691
0
      return 0;
692
0
    }
693
0
    if (pass_obtained < cred->ccache_obtained) {
694
0
      (*error_string) = "The credential cache is invalid";
695
0
      return EINVAL;
696
0
    }
697
0
  }
698
0
  if (cli_credentials_is_anonymous(cred)) {
699
0
    (*error_string) = "Cannot get anonymous kerberos credentials";
700
0
    return EINVAL;
701
0
  }
702
703
0
  ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
704
0
  if (ret) {
705
0
    return ret;
706
0
  }
707
708
0
  ret = kinit_to_ccache(cred,
709
0
            cred,
710
0
            (*ccc)->smb_krb5_context,
711
0
            lp_ctx,
712
0
            event_ctx,
713
0
            (*ccc)->ccache,
714
0
            &obtained,
715
0
            error_string);
716
0
  if (ret) {
717
0
    return ret;
718
0
  }
719
720
0
  ret = cli_credentials_set_from_ccache(cred, *ccc,
721
0
                obtained, error_string);
722
723
0
  cred->ccache = *ccc;
724
0
  cred->ccache_obtained = cred->principal_obtained;
725
0
  if (ret) {
726
0
    return ret;
727
0
  }
728
0
  cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
729
0
  return 0;
730
0
}
731
732
_PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
733
          struct tevent_context *event_ctx,
734
          struct loadparm_context *lp_ctx,
735
          struct ccache_container **ccc,
736
          const char **error_string)
737
0
{
738
0
  return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
739
0
}
740
741
/**
742
 * @brief Check if a valid Kerberos credential cache is attached.
743
 *
744
 * This will not ask for a password nor do a kinit.
745
 *
746
 * @param cred The credentials context.
747
 *
748
 * @param mem_ctx A memory context to allocate the ccache_name.
749
 *
750
 * @param ccache_name A pointer to a string to store the ccache name.
751
 *
752
 * @param obtained A pointer to store the information how the ccache was
753
 *                 obtained.
754
 *
755
 * @return True if a credential cache is attached, false if not or an error
756
 *         occurred.
757
 */
758
_PUBLIC_ bool cli_credentials_get_ccache_name_obtained(
759
  struct cli_credentials *cred,
760
  TALLOC_CTX *mem_ctx,
761
  char **ccache_name,
762
  enum credentials_obtained *obtained)
763
0
{
764
0
  if (ccache_name != NULL) {
765
0
    *ccache_name = NULL;
766
0
  }
767
768
0
  if (obtained != NULL) {
769
0
    *obtained = CRED_UNINITIALISED;
770
0
  }
771
772
0
  if (cred->machine_account_pending) {
773
0
    return false;
774
0
  }
775
776
0
  if (cred->ccache_obtained == CRED_UNINITIALISED) {
777
0
    return false;
778
0
  }
779
780
0
  if (cred->ccache_obtained >= cred->ccache_threshold) {
781
0
    krb5_context k5ctx = cred->ccache->smb_krb5_context->krb5_context;
782
0
    krb5_ccache k5ccache = cred->ccache->ccache;
783
0
    krb5_error_code ret;
784
0
    time_t lifetime = 0;
785
786
0
    ret = smb_krb5_cc_get_lifetime(k5ctx, k5ccache, &lifetime);
787
0
    if (ret == KRB5_CC_END || ret == ENOENT) {
788
0
      return false;
789
0
    }
790
0
    if (ret != 0) {
791
0
      return false;
792
0
    }
793
0
    if (lifetime == 0) {
794
0
      return false;
795
0
    } else if (lifetime < 300) {
796
0
      if (cred->password_obtained >= cred->ccache_obtained) {
797
        /*
798
         * we have a password to re-kinit
799
         * so let the caller try that.
800
         */
801
0
        return false;
802
0
      }
803
0
    }
804
805
0
    if (ccache_name != NULL) {
806
0
      char *name = NULL;
807
808
0
      ret = krb5_cc_get_full_name(k5ctx, k5ccache, &name);
809
0
      if (ret != 0) {
810
0
        return false;
811
0
      }
812
813
0
      *ccache_name = talloc_strdup(mem_ctx, name);
814
0
      SAFE_FREE(name);
815
0
      if (*ccache_name == NULL) {
816
0
        return false;
817
0
      }
818
0
    }
819
820
0
    if (obtained != NULL) {
821
0
      *obtained = cred->ccache_obtained;
822
0
    }
823
824
0
    return true;
825
0
  }
826
827
0
  return false;
828
0
}
829
830
/* We have good reason to think the ccache in these credentials is invalid - blow it away */
831
static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
832
0
{
833
0
  if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
834
0
    talloc_unlink(cred, cred->client_gss_creds);
835
0
    cred->client_gss_creds = NULL;
836
0
  }
837
0
  cred->client_gss_creds_obtained = CRED_UNINITIALISED;
838
0
}
839
840
void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
841
             enum credentials_obtained obtained)
842
761
{
843
  /* If the caller just changed the username/password etc, then
844
   * any cached credentials are now invalid */
845
761
  if (obtained >= cred->client_gss_creds_obtained) {
846
761
    if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
847
0
      talloc_unlink(cred, cred->client_gss_creds);
848
0
      cred->client_gss_creds = NULL;
849
0
    }
850
761
    cred->client_gss_creds_obtained = CRED_UNINITIALISED;
851
761
  }
852
  /* Now that we know that the data is 'this specified', then
853
   * don't allow something less 'known' to be returned as a
854
   * ccache.  Ie, if the username is on the command line, we
855
   * don't want to later guess to use a file-based ccache */
856
761
  if (obtained > cred->client_gss_creds_threshold) {
857
1
    cred->client_gss_creds_threshold = obtained;
858
1
  }
859
761
}
860
861
/* We have good reason to think this CCACHE is invalid.  Blow it away */
862
static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
863
0
{
864
0
  if (cred->ccache_obtained > CRED_UNINITIALISED) {
865
0
    talloc_unlink(cred, cred->ccache);
866
0
    cred->ccache = NULL;
867
0
  }
868
0
  cred->ccache_obtained = CRED_UNINITIALISED;
869
870
0
  cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
871
0
}
872
873
_PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
874
               enum credentials_obtained obtained)
875
761
{
876
  /* If the caller just changed the username/password etc, then
877
   * any cached credentials are now invalid */
878
761
  if (obtained >= cred->ccache_obtained) {
879
761
    if (cred->ccache_obtained > CRED_UNINITIALISED) {
880
0
      talloc_unlink(cred, cred->ccache);
881
0
      cred->ccache = NULL;
882
0
    }
883
761
    cred->ccache_obtained = CRED_UNINITIALISED;
884
761
  }
885
  /* Now that we know that the data is 'this specified', then
886
   * don't allow something less 'known' to be returned as a
887
   * ccache.  i.e, if the username is on the command line, we
888
   * don't want to later guess to use a file-based ccache */
889
761
  if (obtained > cred->ccache_threshold) {
890
1
    cred->ccache_threshold  = obtained;
891
1
  }
892
893
761
  cli_credentials_invalidate_client_gss_creds(cred,
894
761
                obtained);
895
761
}
896
897
static int free_gssapi_creds(struct gssapi_creds_container *gcc)
898
0
{
899
0
  OM_uint32 min_stat;
900
0
  (void)gss_release_cred(&min_stat, &gcc->creds);
901
0
  return 0;
902
0
}
903
904
_PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
905
              struct tevent_context *event_ctx,
906
              struct loadparm_context *lp_ctx,
907
              struct gssapi_creds_container **_gcc,
908
              const char **error_string)
909
0
{
910
0
  int ret = 0;
911
0
  OM_uint32 maj_stat, min_stat;
912
0
  struct gssapi_creds_container *gcc;
913
0
  struct ccache_container *ccache;
914
0
#ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
915
0
  gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
916
0
  gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
917
0
#endif
918
0
  krb5_enctype *etypes = NULL;
919
920
0
  if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
921
0
      cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
922
0
    bool expired = false;
923
0
    OM_uint32 lifetime = 0;
924
0
    gss_cred_usage_t usage = 0;
925
0
    maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
926
0
              NULL, &lifetime, &usage, NULL);
927
0
    if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
928
0
      DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
929
0
      expired = true;
930
0
    } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
931
0
      DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
932
0
      expired = true;
933
0
    } else if (maj_stat != GSS_S_COMPLETE) {
934
0
      *error_string = talloc_asprintf(cred, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n",
935
0
              gssapi_error_string(cred, maj_stat, min_stat, NULL));
936
0
      return EINVAL;
937
0
    }
938
0
    if (expired) {
939
0
      cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
940
0
    } else {
941
0
      DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
942
0
          cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
943
944
0
      *_gcc = cred->client_gss_creds;
945
0
      return 0;
946
0
    }
947
0
  }
948
949
0
  ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
950
0
           &ccache, error_string);
951
0
  if (ret) {
952
0
    if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
953
0
      DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
954
0
    } else {
955
0
      DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
956
0
    }
957
0
    return ret;
958
0
  }
959
960
0
  gcc = talloc(cred, struct gssapi_creds_container);
961
0
  if (!gcc) {
962
0
    (*error_string) = error_message(ENOMEM);
963
0
    return ENOMEM;
964
0
  }
965
966
0
  maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
967
0
              ccache->ccache, NULL, NULL,
968
0
              &gcc->creds);
969
0
  if ((maj_stat == GSS_S_FAILURE) &&
970
0
      (min_stat == (OM_uint32)KRB5_CC_END ||
971
0
       min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
972
0
       min_stat == (OM_uint32)KRB5_FCC_NOFILE))
973
0
  {
974
    /* This CCACHE is no good.  Ensure we don't use it again */
975
0
    cli_credentials_unconditionally_invalidate_ccache(cred);
976
977
    /* Now try again to get a ccache */
978
0
    ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
979
0
             &ccache, error_string);
980
0
    if (ret) {
981
0
      DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
982
0
      return ret;
983
0
    }
984
985
0
    maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
986
0
                ccache->ccache, NULL, NULL,
987
0
                &gcc->creds);
988
989
0
  }
990
991
0
  if (maj_stat) {
992
0
    talloc_free(gcc);
993
0
    if (min_stat) {
994
0
      ret = min_stat;
995
0
    } else {
996
0
      ret = EINVAL;
997
0
    }
998
0
    (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
999
0
    return ret;
1000
0
  }
1001
1002
1003
  /*
1004
   * transfer the enctypes from the smb_krb5_context to the gssapi layer
1005
   *
1006
   * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
1007
   * to configure the enctypes via the krb5.conf.
1008
   *
1009
   * And the gss_init_sec_context() creates it's own krb5_context and
1010
   * the TGS-REQ had all enctypes in it and only the ones configured
1011
   * and used for the AS-REQ, so it wasn't possible to disable the usage
1012
   * of AES keys.
1013
   */
1014
0
  min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
1015
0
                 &etypes);
1016
0
  if (min_stat == 0) {
1017
0
    OM_uint32 num_ktypes;
1018
1019
0
    for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
1020
1021
0
    maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
1022
0
                 num_ktypes,
1023
0
                 (int32_t *) etypes);
1024
0
    krb5_free_enctypes(ccache->smb_krb5_context->krb5_context,
1025
0
           etypes);
1026
0
    if (maj_stat) {
1027
0
      talloc_free(gcc);
1028
0
      if (min_stat) {
1029
0
        ret = min_stat;
1030
0
      } else {
1031
0
        ret = EINVAL;
1032
0
      }
1033
0
      (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
1034
0
      return ret;
1035
0
    }
1036
0
  }
1037
1038
0
#ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
1039
  /*
1040
   * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
1041
   *
1042
   * This allows us to disable SIGN and SEAL on a TLS connection with
1043
   * GSS-SPNENO. For example ldaps:// connections.
1044
   *
1045
   * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
1046
   * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
1047
   */
1048
0
  maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
1049
0
               oid,
1050
0
               &empty_buffer);
1051
0
  if (maj_stat) {
1052
0
    talloc_free(gcc);
1053
0
    if (min_stat) {
1054
0
      ret = min_stat;
1055
0
    } else {
1056
0
      ret = EINVAL;
1057
0
    }
1058
0
    (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
1059
0
    return ret;
1060
0
  }
1061
0
#endif
1062
0
  cred->client_gss_creds_obtained = cred->ccache_obtained;
1063
0
  talloc_set_destructor(gcc, free_gssapi_creds);
1064
0
  cred->client_gss_creds = gcc;
1065
0
  *_gcc = gcc;
1066
0
  return 0;
1067
0
}
1068
1069
/**
1070
   Set a gssapi cred_id_t into the credentials system. (Client case)
1071
1072
   This grabs the credentials both 'intact' and getting the krb5
1073
   ccache out of it.  This routine can be generalised in future for
1074
   the case where we deal with GSSAPI mechs other than krb5.
1075
1076
   On success, the caller must not free gssapi_cred, as it now belongs
1077
   to the credentials system.
1078
*/
1079
1080
 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
1081
            struct loadparm_context *lp_ctx,
1082
            gss_cred_id_t gssapi_cred,
1083
            enum credentials_obtained obtained,
1084
            const char **error_string)
1085
0
{
1086
0
  int ret;
1087
0
  OM_uint32 maj_stat, min_stat;
1088
0
  struct ccache_container *ccc = NULL;
1089
0
  struct gssapi_creds_container *gcc = NULL;
1090
0
  if (cred->client_gss_creds_obtained > obtained) {
1091
0
    return 0;
1092
0
  }
1093
1094
0
  gcc = talloc(cred, struct gssapi_creds_container);
1095
0
  if (!gcc) {
1096
0
    (*error_string) = error_message(ENOMEM);
1097
0
    return ENOMEM;
1098
0
  }
1099
1100
0
  ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1101
0
  if (ret != 0) {
1102
0
    return ret;
1103
0
  }
1104
1105
0
  maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1106
0
              gssapi_cred,
1107
0
              ccc);
1108
0
  if (maj_stat) {
1109
0
    if (min_stat) {
1110
0
      ret = min_stat;
1111
0
    } else {
1112
0
      ret = EINVAL;
1113
0
    }
1114
0
    if (ret) {
1115
0
      (*error_string) = error_message(ENOMEM);
1116
0
    }
1117
0
  }
1118
1119
0
  if (ret == 0) {
1120
0
    ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1121
0
  }
1122
0
  cred->ccache = ccc;
1123
0
  cred->ccache_obtained = obtained;
1124
0
  if (ret == 0) {
1125
0
    gcc->creds = gssapi_cred;
1126
0
    talloc_set_destructor(gcc, free_gssapi_creds);
1127
1128
    /* set the client_gss_creds_obtained here, as it just
1129
       got set to UNINITIALISED by the calls above */
1130
0
    cred->client_gss_creds_obtained = obtained;
1131
0
    cred->client_gss_creds = gcc;
1132
0
  }
1133
0
  return ret;
1134
0
}
1135
1136
static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1137
0
{
1138
0
  krb5_error_code ret;
1139
0
  const struct ccache_container *old_ccc = NULL;
1140
0
  enum credentials_obtained old_obtained;
1141
0
  struct ccache_container *ccc = NULL;
1142
0
  krb5_principal princ;
1143
1144
0
  old_obtained = cred->ccache_obtained;
1145
0
  old_ccc = cred->ccache;
1146
0
  if (old_ccc == NULL) {
1147
0
    return 0;
1148
0
  }
1149
1150
0
  cred->ccache = NULL;
1151
0
  cred->ccache_obtained = CRED_UNINITIALISED;
1152
0
  cred->client_gss_creds = NULL;
1153
0
  cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1154
1155
0
  ret = krb5_cc_get_principal(
1156
0
    old_ccc->smb_krb5_context->krb5_context,
1157
0
    old_ccc->ccache,
1158
0
    &princ);
1159
0
  if (ret != 0) {
1160
    /*
1161
     * This is an empty ccache. No point in copying anything.
1162
     */
1163
0
    return 0;
1164
0
  }
1165
0
  krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1166
1167
0
  ccc = talloc(cred, struct ccache_container);
1168
0
  if (ccc == NULL) {
1169
0
    return ENOMEM;
1170
0
  }
1171
0
  *ccc = *old_ccc;
1172
0
  ccc->ccache = NULL;
1173
1174
0
  ret = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
1175
0
              NULL,
1176
0
              NULL,
1177
0
              &ccc->ccache);
1178
0
  if (ret != 0) {
1179
0
    TALLOC_FREE(ccc);
1180
0
    return ret;
1181
0
  }
1182
1183
0
  talloc_set_destructor(ccc, free_mccache);
1184
1185
0
  ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1186
0
             old_ccc->ccache, ccc->ccache);
1187
0
  if (ret != 0) {
1188
0
    TALLOC_FREE(ccc);
1189
0
    return ret;
1190
0
  }
1191
1192
0
  cred->ccache = ccc;
1193
0
  cred->ccache_obtained = old_obtained;
1194
0
  return ret;
1195
0
}
1196
1197
_PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1198
            struct cli_credentials *src)
1199
0
{
1200
0
  struct cli_credentials *dst, *armor_credentials;
1201
0
  int ret;
1202
1203
0
  dst = talloc(mem_ctx, struct cli_credentials);
1204
0
  if (dst == NULL) {
1205
0
    return NULL;
1206
0
  }
1207
1208
0
  *dst = *src;
1209
1210
0
  if (dst->krb5_fast_armor_credentials != NULL) {
1211
0
    armor_credentials = talloc_reference(dst, dst->krb5_fast_armor_credentials);
1212
0
    if (armor_credentials == NULL) {
1213
0
      TALLOC_FREE(dst);
1214
0
      return NULL;
1215
0
    }
1216
0
  }
1217
1218
0
  ret = cli_credentials_shallow_ccache(dst);
1219
0
  if (ret != 0) {
1220
0
    TALLOC_FREE(dst);
1221
0
    return NULL;
1222
0
  }
1223
1224
0
  return dst;
1225
0
}
1226
1227
/* Get the keytab (actually, a container containing the krb5_keytab)
1228
 * attached to this context.  If this hasn't been done or set before,
1229
 * it will be generated from the password.
1230
 */
1231
_PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1232
          struct loadparm_context *lp_ctx,
1233
          struct keytab_container **_ktc)
1234
0
{
1235
0
  krb5_error_code ret;
1236
0
  struct keytab_container *ktc;
1237
0
  struct smb_krb5_context *smb_krb5_context;
1238
0
  const char *keytab_name;
1239
0
  krb5_keytab keytab;
1240
0
  TALLOC_CTX *mem_ctx;
1241
0
  const char *username = cli_credentials_get_username(cred);
1242
0
  const char *realm = cli_credentials_get_realm(cred);
1243
0
  char *salt_principal = NULL;
1244
1245
0
  if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1246
0
            cred->username_obtained))) {
1247
0
    *_ktc = cred->keytab;
1248
0
    return 0;
1249
0
  }
1250
1251
0
  if (cli_credentials_is_anonymous(cred)) {
1252
0
    return EINVAL;
1253
0
  }
1254
1255
0
  ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1256
0
                 &smb_krb5_context);
1257
0
  if (ret) {
1258
0
    return ret;
1259
0
  }
1260
1261
0
  mem_ctx = talloc_new(cred);
1262
0
  if (!mem_ctx) {
1263
0
    return ENOMEM;
1264
0
  }
1265
1266
0
  salt_principal = cli_credentials_get_salt_principal(cred, mem_ctx);
1267
0
  if (salt_principal == NULL) {
1268
0
    talloc_free(mem_ctx);
1269
0
    return ENOMEM;
1270
0
  }
1271
1272
0
  ret = smb_krb5_create_memory_keytab(mem_ctx,
1273
0
              smb_krb5_context->krb5_context,
1274
0
              cli_credentials_get_password(cred),
1275
0
              username,
1276
0
              realm,
1277
0
              salt_principal,
1278
0
              cli_credentials_get_kvno(cred),
1279
0
              &keytab,
1280
0
              &keytab_name);
1281
0
  if (ret) {
1282
0
    talloc_free(mem_ctx);
1283
0
    return ret;
1284
0
  }
1285
1286
0
  ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1287
0
              keytab, keytab_name, &ktc);
1288
0
  if (ret) {
1289
0
    talloc_free(mem_ctx);
1290
0
    return ret;
1291
0
  }
1292
1293
0
  cred->keytab_obtained = (MAX(cred->principal_obtained,
1294
0
             cred->username_obtained));
1295
1296
  /* We make this keytab up based on a password.  Therefore
1297
   * match-by-key is acceptable, we can't match on the wrong
1298
   * principal */
1299
0
  ktc->password_based = true;
1300
1301
0
  talloc_steal(cred, ktc);
1302
0
  cred->keytab = ktc;
1303
0
  *_ktc = cred->keytab;
1304
0
  talloc_free(mem_ctx);
1305
0
  return ret;
1306
0
}
1307
1308
/* Given the name of a keytab (presumably in the format
1309
 * FILE:/etc/krb5.keytab), open it and attach it */
1310
1311
_PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1312
               struct loadparm_context *lp_ctx,
1313
               const char *keytab_name,
1314
               enum credentials_obtained obtained)
1315
0
{
1316
0
  krb5_error_code ret;
1317
0
  struct keytab_container *ktc;
1318
0
  struct smb_krb5_context *smb_krb5_context;
1319
0
  TALLOC_CTX *mem_ctx;
1320
1321
0
  if (cred->keytab_obtained >= obtained) {
1322
0
    return 0;
1323
0
  }
1324
1325
0
  ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1326
0
  if (ret) {
1327
0
    return ret;
1328
0
  }
1329
1330
0
  mem_ctx = talloc_new(cred);
1331
0
  if (!mem_ctx) {
1332
0
    return ENOMEM;
1333
0
  }
1334
1335
0
  ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1336
0
              NULL, keytab_name, &ktc);
1337
0
  if (ret) {
1338
0
    return ret;
1339
0
  }
1340
1341
0
  cred->keytab_obtained = obtained;
1342
1343
0
  talloc_steal(cred, ktc);
1344
0
  cred->keytab = ktc;
1345
0
  talloc_free(mem_ctx);
1346
1347
0
  return ret;
1348
0
}
1349
1350
/* Get server gss credentials (in gsskrb5, this means the keytab) */
1351
1352
_PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1353
              struct loadparm_context *lp_ctx,
1354
              struct gssapi_creds_container **_gcc)
1355
0
{
1356
0
  int ret = 0;
1357
0
  OM_uint32 maj_stat, min_stat;
1358
0
  struct gssapi_creds_container *gcc;
1359
0
  struct keytab_container *ktc;
1360
0
  struct smb_krb5_context *smb_krb5_context;
1361
0
  TALLOC_CTX *mem_ctx;
1362
0
  krb5_principal princ;
1363
0
  const char *error_string;
1364
0
  enum credentials_obtained obtained;
1365
1366
0
  mem_ctx = talloc_new(cred);
1367
0
  if (!mem_ctx) {
1368
0
    return ENOMEM;
1369
0
  }
1370
1371
0
  ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1372
0
  if (ret) {
1373
0
    return ret;
1374
0
  }
1375
1376
0
  ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1377
0
  if (ret) {
1378
0
    DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1379
0
       error_string));
1380
0
    talloc_free(mem_ctx);
1381
0
    return ret;
1382
0
  }
1383
1384
0
  if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1385
0
    talloc_free(mem_ctx);
1386
0
    *_gcc = cred->server_gss_creds;
1387
0
    return 0;
1388
0
  }
1389
1390
0
  ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1391
0
  if (ret) {
1392
0
    DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1393
0
    return ret;
1394
0
  }
1395
1396
0
  gcc = talloc(cred, struct gssapi_creds_container);
1397
0
  if (!gcc) {
1398
0
    talloc_free(mem_ctx);
1399
0
    return ENOMEM;
1400
0
  }
1401
1402
0
  if (ktc->password_based || obtained < CRED_SPECIFIED) {
1403
    /*
1404
     * This creates a GSSAPI cred_id_t for match-by-key with only
1405
     * the keytab set
1406
     */
1407
0
    princ = NULL;
1408
0
  }
1409
0
  maj_stat = smb_gss_krb5_import_cred(&min_stat,
1410
0
              smb_krb5_context->krb5_context,
1411
0
              NULL, princ,
1412
0
              ktc->keytab,
1413
0
              &gcc->creds);
1414
0
  if (maj_stat) {
1415
0
    if (min_stat) {
1416
0
      ret = min_stat;
1417
0
    } else {
1418
0
      ret = EINVAL;
1419
0
    }
1420
0
  }
1421
0
  if (ret == 0) {
1422
0
    cred->server_gss_creds_obtained = cred->keytab_obtained;
1423
0
    talloc_set_destructor(gcc, free_gssapi_creds);
1424
0
    cred->server_gss_creds = gcc;
1425
0
    *_gcc = gcc;
1426
0
  }
1427
0
  talloc_free(mem_ctx);
1428
0
  return ret;
1429
0
}
1430
1431
/**
1432
 * Set Kerberos KVNO
1433
 */
1434
1435
_PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1436
            int kvno)
1437
0
{
1438
0
  cred->kvno = kvno;
1439
0
}
1440
1441
/**
1442
 * Return Kerberos KVNO
1443
 */
1444
1445
_PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1446
0
{
1447
0
  return cred->kvno;
1448
0
}
1449
1450
1451
char *cli_credentials_get_salt_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
1452
0
{
1453
0
  TALLOC_CTX *frame = NULL;
1454
0
  const char *realm = NULL;
1455
0
  const char *username = NULL;
1456
0
  uint32_t uac_flags = 0;
1457
0
  char *salt_principal = NULL;
1458
0
  const char *upn = NULL;
1459
0
  int ret;
1460
1461
  /* If specified, use the specified value */
1462
0
  if (cred->salt_principal != NULL) {
1463
0
    return talloc_strdup(mem_ctx, cred->salt_principal);
1464
0
  }
1465
1466
0
  frame = talloc_stackframe();
1467
1468
0
  switch (cred->secure_channel_type) {
1469
0
  case SEC_CHAN_WKSTA:
1470
0
  case SEC_CHAN_RODC:
1471
0
    uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1472
0
    break;
1473
0
  case SEC_CHAN_BDC:
1474
0
    uac_flags = UF_SERVER_TRUST_ACCOUNT;
1475
0
    break;
1476
0
  case SEC_CHAN_DOMAIN:
1477
0
  case SEC_CHAN_DNS_DOMAIN:
1478
0
    uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1479
0
    break;
1480
0
  default:
1481
0
    upn = cli_credentials_get_principal(cred, frame);
1482
0
    if (upn == NULL) {
1483
0
      TALLOC_FREE(frame);
1484
0
      return NULL;
1485
0
    }
1486
0
    uac_flags = UF_NORMAL_ACCOUNT;
1487
0
    break;
1488
0
  }
1489
1490
0
  realm = cli_credentials_get_realm(cred);
1491
0
  username = cli_credentials_get_username(cred);
1492
1493
0
  ret = smb_krb5_salt_principal_str(realm,
1494
0
            username, /* sAMAccountName */
1495
0
            upn, /* userPrincipalName */
1496
0
            uac_flags,
1497
0
            mem_ctx,
1498
0
            &salt_principal);
1499
0
  if (ret) {
1500
0
    TALLOC_FREE(frame);
1501
0
    return NULL;
1502
0
  }
1503
1504
0
  TALLOC_FREE(frame);
1505
0
  return salt_principal;
1506
0
}
1507
1508
_PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1509
0
{
1510
0
  talloc_free(cred->salt_principal);
1511
0
  cred->salt_principal = talloc_strdup(cred, principal);
1512
0
}
1513
1514
/* The 'impersonate_principal' is used to allow one Kerberos principal
1515
 * (and it's associated keytab etc) to impersonate another.  The
1516
 * ability to do this is controlled by the KDC, but it is generally
1517
 * permitted to impersonate anyone to yourself.  This allows any
1518
 * member of the domain to get the groups of a user.  This is also
1519
 * known as S4U2Self */
1520
1521
_PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1522
0
{
1523
0
  return cred->impersonate_principal;
1524
0
}
1525
1526
/*
1527
 * The 'self_service' is the service principal that
1528
 * represents the same object (by its objectSid)
1529
 * as the client principal (typically our machine account).
1530
 * When trying to impersonate 'impersonate_principal' with
1531
 * S4U2Self.
1532
 */
1533
_PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1534
0
{
1535
0
  return cred->self_service;
1536
0
}
1537
1538
_PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1539
              const char *principal,
1540
              const char *self_service)
1541
0
{
1542
0
  talloc_free(cred->impersonate_principal);
1543
0
  cred->impersonate_principal = talloc_strdup(cred, principal);
1544
0
  talloc_free(cred->self_service);
1545
0
  cred->self_service = talloc_strdup(cred, self_service);
1546
0
  cli_credentials_set_kerberos_state(cred,
1547
0
             CRED_USE_KERBEROS_REQUIRED,
1548
0
             CRED_SPECIFIED);
1549
0
}
1550
1551
/*
1552
 * when impersonating for S4U2proxy we need to set the target principal.
1553
 * Similarly, we may only be authorized to do general impersonation to
1554
 * some particular services.
1555
 *
1556
 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1557
 *
1558
 * NULL means that tickets will be obtained for the krbtgt service.
1559
*/
1560
1561
const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1562
0
{
1563
0
  return cred->target_service;
1564
0
}
1565
1566
_PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1567
0
{
1568
0
  talloc_free(cred->target_service);
1569
0
  cred->target_service = talloc_strdup(cred, target_service);
1570
0
}
1571
1572
_PUBLIC_ int cli_credentials_get_kerberos_key(struct cli_credentials *cred,
1573
                TALLOC_CTX *mem_ctx,
1574
                struct loadparm_context *lp_ctx,
1575
                krb5_enctype enctype,
1576
                bool previous,
1577
                DATA_BLOB *key_blob)
1578
0
{
1579
0
  struct smb_krb5_context *smb_krb5_context = NULL;
1580
0
  krb5_error_code krb5_ret;
1581
0
  int ret;
1582
0
  const char *password = NULL;
1583
0
  const char *salt = NULL;
1584
0
  krb5_data cleartext_data;
1585
0
  krb5_data salt_data = {
1586
0
    .length = 0,
1587
0
  };
1588
0
  krb5_keyblock key;
1589
1590
0
  TALLOC_CTX *frame = talloc_stackframe();
1591
1592
0
  if ((int)enctype == (int)ENCTYPE_ARCFOUR_HMAC) {
1593
0
    struct samr_Password *nt_hash;
1594
1595
0
    if (previous) {
1596
0
      nt_hash = cli_credentials_get_old_nt_hash(cred, frame);
1597
0
    } else {
1598
0
      nt_hash = cli_credentials_get_nt_hash(cred, frame);
1599
0
    }
1600
1601
0
    if (nt_hash == NULL) {
1602
0
      TALLOC_FREE(frame);
1603
0
      return EINVAL;
1604
0
    }
1605
0
    *key_blob = data_blob_talloc(mem_ctx,
1606
0
               nt_hash->hash,
1607
0
               sizeof(nt_hash->hash));
1608
0
    if (key_blob->data == NULL) {
1609
0
      TALLOC_FREE(frame);
1610
0
      return ENOMEM;
1611
0
    }
1612
0
    TALLOC_FREE(frame);
1613
0
    return 0;
1614
0
  }
1615
1616
0
  if (cred->password_will_be_nt_hash) {
1617
0
    DEBUG(1,("cli_credentials_get_kerberos_key: cannot generate Kerberos key using NT hash\n"));
1618
0
    TALLOC_FREE(frame);
1619
0
    return EINVAL;
1620
0
  }
1621
1622
0
  salt = cli_credentials_get_salt_principal(cred, frame);
1623
0
  if (salt == NULL) {
1624
0
    TALLOC_FREE(frame);
1625
0
    return EINVAL;
1626
0
  }
1627
1628
0
  if (previous) {
1629
0
    password = cli_credentials_get_old_password(cred);
1630
0
  } else {
1631
0
    password = cli_credentials_get_password(cred);
1632
0
  }
1633
0
  if (password == NULL) {
1634
0
    TALLOC_FREE(frame);
1635
0
    return EINVAL;
1636
0
  }
1637
1638
0
  cleartext_data.data = discard_const_p(char, password);
1639
0
  cleartext_data.length = strlen(password);
1640
1641
0
  ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1642
0
                 &smb_krb5_context);
1643
0
  if (ret != 0) {
1644
0
    TALLOC_FREE(frame);
1645
0
    return ret;
1646
0
  }
1647
1648
0
  salt_data.data = discard_const_p(char, salt);
1649
0
  salt_data.length = strlen(salt);
1650
1651
  /*
1652
   * create Kerberos key out of
1653
   * the salt and the cleartext password
1654
   */
1655
0
  krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
1656
0
               NULL,
1657
0
               &salt_data,
1658
0
               &cleartext_data,
1659
0
               enctype,
1660
0
               &key);
1661
0
  if (krb5_ret != 0) {
1662
0
    DEBUG(1,("cli_credentials_get_aes256_key: "
1663
0
       "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
1664
0
       smb_get_krb5_error_message(smb_krb5_context->krb5_context,
1665
0
                krb5_ret, mem_ctx)));
1666
0
    TALLOC_FREE(frame);
1667
0
    return EINVAL;
1668
0
  }
1669
0
  *key_blob = data_blob_talloc(mem_ctx,
1670
0
            KRB5_KEY_DATA(&key),
1671
0
            KRB5_KEY_LENGTH(&key));
1672
0
  krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
1673
0
  if (key_blob->data == NULL) {
1674
0
    TALLOC_FREE(frame);
1675
0
    return ENOMEM;
1676
0
  }
1677
0
  talloc_keep_secret(key_blob->data);
1678
1679
0
  TALLOC_FREE(frame);
1680
0
  return 0;
1681
0
}
1682
1683
/* This take a reference to the armor credentials to ensure the lifetime is appropriate */
1684
1685
NTSTATUS cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials *creds,
1686
               struct cli_credentials *armor_creds,
1687
               bool require_fast_armor)
1688
0
{
1689
0
  talloc_unlink(creds, creds->krb5_fast_armor_credentials);
1690
0
  if (armor_creds == NULL) {
1691
0
    creds->krb5_fast_armor_credentials = NULL;
1692
0
    return NT_STATUS_OK;
1693
0
  }
1694
1695
0
  creds->krb5_fast_armor_credentials = talloc_reference(creds, armor_creds);
1696
0
  if (creds->krb5_fast_armor_credentials == NULL) {
1697
0
    return NT_STATUS_NO_MEMORY;
1698
0
  }
1699
1700
0
  creds->krb5_require_fast_armor = require_fast_armor;
1701
1702
0
  return NT_STATUS_OK;
1703
0
}
1704
1705
struct cli_credentials *cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials *creds)
1706
0
{
1707
0
  return creds->krb5_fast_armor_credentials;
1708
0
}
1709
1710
bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials *creds)
1711
0
{
1712
0
  return creds->krb5_require_fast_armor;
1713
0
}