Coverage Report

Created: 2026-01-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/krb5_wrap/krb5_samba.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   simple kerberos5 routines for active directory
4
   Copyright (C) Andrew Tridgell 2001
5
   Copyright (C) Luke Howard 2002-2003
6
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
7
   Copyright (C) Guenther Deschner 2005-2009
8
9
   This program is free software; you can redistribute it and/or modify
10
   it under the terms of the GNU General Public License as published by
11
   the Free Software Foundation; either version 3 of the License, or
12
   (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
*/
22
23
#include "includes.h"
24
#include "system/filesys.h"
25
#include "krb5_samba.h"
26
#include "lib/crypto/md4.h"
27
#include "../libds/common/flags.h"
28
29
#ifdef HAVE_COM_ERR_H
30
#include <com_err.h>
31
#endif /* HAVE_COM_ERR_H */
32
33
#ifndef KRB5_AUTHDATA_WIN2K_PAC
34
#define KRB5_AUTHDATA_WIN2K_PAC 128
35
#endif
36
37
#ifndef KRB5_AUTHDATA_IF_RELEVANT
38
#define KRB5_AUTHDATA_IF_RELEVANT 1
39
#endif
40
41
#ifdef HAVE_KRB5
42
43
#define GSSAPI_CHECKSUM      0x8003             /* Checksum type value for Kerberos */
44
#define GSSAPI_BNDLENGTH     16                 /* Bind Length (rfc-1964 pg.3) */
45
#define GSSAPI_CHECKSUM_SIZE (4+GSSAPI_BNDLENGTH+4) /* Length of bind length,
46
              bind field, flags field. */
47
#define GSS_C_DELEG_FLAG 1
48
49
/* MIT krb5 1.7beta3 (in Ubuntu Karmic) is missing the prototype,
50
   but still has the symbol */
51
#if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE
52
krb5_error_code krb5_auth_con_set_req_cksumtype(
53
  krb5_context     context,
54
  krb5_auth_context      auth_context,
55
  krb5_cksumtype     cksumtype);
56
#endif
57
58
#if !defined(SMB_MALLOC)
59
#undef malloc
60
0
#define SMB_MALLOC(s) malloc((s))
61
#endif
62
63
#ifndef SMB_STRDUP
64
#define SMB_STRDUP(s) strdup(s)
65
#endif
66
67
/**********************************************************
68
 * MISSING FUNCTIONS
69
 **********************************************************/
70
71
#if !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
72
73
#if defined(HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES)
74
75
/* With MIT kerberos, we should use krb5_set_default_tgs_enctypes in preference
76
 * to krb5_set_default_tgs_ktypes. See
77
 *         http://lists.samba.org/archive/samba-technical/2006-July/048271.html
78
 *
79
 * If the MIT libraries are not exporting internal symbols, we will end up in
80
 * this branch, which is correct. Otherwise we will continue to use the
81
 * internal symbol
82
 */
83
 krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
84
{
85
    return krb5_set_default_tgs_enctypes(ctx, enc);
86
}
87
88
#elif defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES)
89
90
/* Heimdal */
91
 krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
92
0
{
93
0
  return krb5_set_default_in_tkt_etypes(ctx, enc);
94
0
}
95
96
#endif /* HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES */
97
98
#endif /* HAVE_KRB5_SET_DEFAULT_TGS_KTYPES */
99
100
101
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
102
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
103
               krb5_auth_context auth_context,
104
               krb5_keyblock *keyblock)
105
0
{
106
0
  return krb5_auth_con_setkey(context, auth_context, keyblock);
107
0
}
108
#endif
109
110
#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
111
void krb5_free_unparsed_name(krb5_context context, char *val)
112
{
113
  SAFE_FREE(val);
114
}
115
#endif
116
117
#if !defined(HAVE_KRB5_FREE_ENCTYPES)
118
0
void krb5_free_enctypes(krb5_context context, krb5_enctype *val) {
119
0
  krb5_xfree(val);
120
0
}
121
#endif
122
123
#if !defined(HAVE_KRB5_FREE_STRING)
124
0
void krb5_free_string(krb5_context context, char *val) {
125
0
  SAFE_FREE(val);
126
0
}
127
#endif
128
129
krb5_error_code smb_krb5_princ_component(krb5_context context,
130
           krb5_const_principal principal,
131
           int i,
132
           krb5_data *data);
133
krb5_error_code smb_krb5_princ_component(krb5_context context,
134
           krb5_const_principal principal,
135
           int i,
136
           krb5_data *data)
137
0
{
138
0
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
139
0
  const char *component = NULL;
140
141
0
  if (i < 0) {
142
0
    return EINVAL;
143
0
  }
144
145
0
  component = krb5_principal_get_comp_string(context, principal, i);
146
0
  if (component == NULL) {
147
0
    return ENOENT;
148
0
  }
149
150
0
  *data = smb_krb5_make_data(discard_const_p(char, component), strlen(component));
151
152
0
  return 0;
153
#else
154
  const krb5_data *kdata = NULL;
155
156
  if (i < 0) {
157
    return EINVAL;
158
  }
159
160
  kdata = krb5_princ_component(context, principal, i);
161
  if (kdata == NULL) {
162
    return ENOENT;
163
  }
164
165
  *data = *kdata;
166
167
  return 0;
168
#endif
169
0
}
170
171
/**********************************************************
172
 * WRAPPING FUNCTIONS
173
 **********************************************************/
174
175
/**
176
 * @brief Stores the address of a 'struct sockaddr_storage' into a krb5_address
177
 *
178
 * @param[in]  paddr    A pointer to a 'struct sockaddr_storage to extract the
179
 *                      address from.
180
 *
181
 * @param[out] pkaddr   A Kerberos address to store the address in.
182
 *
183
 * @return True on success, false if an error occurred.
184
 */
185
bool smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage *paddr,
186
        krb5_address *pkaddr)
187
0
{
188
0
  memset(pkaddr, '\0', sizeof(krb5_address));
189
0
#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
190
/* HEIMDAL */
191
0
#ifdef HAVE_IPV6
192
0
  if (paddr->ss_family == AF_INET6) {
193
0
    pkaddr->addr_type = KRB5_ADDRESS_INET6;
194
0
    pkaddr->address.length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
195
0
    pkaddr->address.data = (char *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
196
0
    return true;
197
0
  }
198
0
#endif
199
0
  if (paddr->ss_family == AF_INET) {
200
0
    pkaddr->addr_type = KRB5_ADDRESS_INET;
201
0
    pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
202
0
    pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
203
0
    return true;
204
0
  }
205
#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
206
/* MIT */
207
#ifdef HAVE_IPV6
208
  if (paddr->ss_family == AF_INET6) {
209
    pkaddr->addrtype = ADDRTYPE_INET6;
210
    pkaddr->length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
211
    pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
212
    return true;
213
  }
214
#endif
215
  if (paddr->ss_family == AF_INET) {
216
    pkaddr->addrtype = ADDRTYPE_INET;
217
    pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
218
    pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
219
    return true;
220
  }
221
#else
222
#error UNKNOWN_ADDRTYPE
223
#endif
224
0
  return false;
225
0
}
226
227
krb5_error_code smb_krb5_mk_error(krb5_context context,
228
          krb5_error_code error_code,
229
          const char *e_text,
230
          krb5_data *e_data,
231
          const krb5_principal client,
232
          const krb5_principal server,
233
          krb5_data *enc_err)
234
0
{
235
0
  krb5_error_code code = EINVAL;
236
0
#ifdef SAMBA4_USES_HEIMDAL
237
0
  code = krb5_mk_error(context,
238
0
           error_code,
239
0
           e_text,
240
0
           e_data,
241
0
           client,
242
0
           server,
243
0
           NULL, /* client_time */
244
0
           NULL, /* client_usec */
245
0
           enc_err);
246
#else
247
  krb5_principal unspec_server = NULL;
248
  krb5_error errpkt;
249
250
  errpkt.ctime = 0;
251
  errpkt.cusec = 0;
252
253
  code = krb5_us_timeofday(context,
254
         &errpkt.stime,
255
         &errpkt.susec);
256
  if (code != 0) {
257
    return code;
258
  }
259
260
  errpkt.error = error_code - ERROR_TABLE_BASE_krb5;
261
262
  errpkt.text.length = 0;
263
  if (e_text != NULL) {
264
    errpkt.text = smb_krb5_make_data(discard_const_p(char, e_text), strlen(e_text));
265
  }
266
267
  errpkt.e_data = smb_krb5_make_data(NULL, 0);
268
  if (e_data != NULL) {
269
    errpkt.e_data = *e_data;
270
  }
271
272
  errpkt.client = client;
273
274
  if (server != NULL) {
275
    errpkt.server = server;
276
  } else {
277
    code = smb_krb5_make_principal(context,
278
                 &unspec_server,
279
                 "<unspecified realm>",
280
                 NULL);
281
    if (code != 0) {
282
      return code;
283
    }
284
    errpkt.server = unspec_server;
285
  }
286
287
  code = krb5_mk_error(context,
288
           &errpkt,
289
           enc_err);
290
  krb5_free_principal(context, unspec_server);
291
#endif
292
0
  return code;
293
0
}
294
295
/**
296
* @brief Create a keyblock based on input parameters
297
*
298
* @param context  The krb5_context
299
* @param host_princ The krb5_principal to use
300
* @param salt   The optional salt, if omitted, salt is calculated with
301
*     the provided principal.
302
* @param password The krb5_data containing the password
303
* @param enctype  The krb5_enctype to use for the keyblock generation
304
* @param key    The returned krb5_keyblock, caller needs to free with
305
*     krb5_free_keyblock().
306
*
307
* @return krb5_error_code
308
*/
309
int smb_krb5_create_key_from_string(krb5_context context,
310
            krb5_const_principal host_princ,
311
            const krb5_data *salt,
312
            const krb5_data *password,
313
            krb5_enctype enctype,
314
            krb5_keyblock *key)
315
0
{
316
0
  int ret = 0;
317
318
0
  if (host_princ == NULL && salt == NULL) {
319
0
    return -1;
320
0
  }
321
322
0
  if ((int)enctype == (int)ENCTYPE_ARCFOUR_HMAC) {
323
0
    TALLOC_CTX *frame = talloc_stackframe();
324
0
    uint8_t *utf16 = NULL;
325
0
    size_t utf16_size = 0;
326
0
    uint8_t nt_hash[16];
327
0
    bool ok;
328
329
0
    ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16LE,
330
0
             password->data, password->length,
331
0
             &utf16, &utf16_size);
332
0
    if (!ok) {
333
0
      if (errno == 0) {
334
0
        errno = EINVAL;
335
0
      }
336
0
      ret = errno;
337
0
      TALLOC_FREE(frame);
338
0
      return ret;
339
0
    }
340
341
0
    mdfour(nt_hash, utf16, utf16_size);
342
0
    BURN_PTR_SIZE(utf16, utf16_size);
343
0
    ret = smb_krb5_keyblock_init_contents(context,
344
0
                  ENCTYPE_ARCFOUR_HMAC,
345
0
                  nt_hash,
346
0
                  sizeof(nt_hash),
347
0
                  key);
348
0
    ZERO_STRUCT(nt_hash);
349
0
    if (ret != 0) {
350
0
      TALLOC_FREE(frame);
351
0
      return ret;
352
0
    }
353
354
0
    TALLOC_FREE(frame);
355
0
    return 0;
356
0
  }
357
358
#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_C_STRING_TO_KEY)
359
{/* MIT */
360
  krb5_data _salt;
361
362
  if (salt == NULL) {
363
    ret = krb5_principal2salt(context, host_princ, &_salt);
364
    if (ret) {
365
      DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
366
      return ret;
367
    }
368
  } else {
369
    _salt = *salt;
370
  }
371
  ret = krb5_c_string_to_key(context, enctype, password, &_salt, key);
372
  if (salt == NULL) {
373
    SAFE_FREE(_salt.data);
374
  }
375
}
376
#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
377
0
{/* Heimdal */
378
0
  krb5_salt _salt;
379
380
0
  if (salt == NULL) {
381
0
    ret = krb5_get_pw_salt(context, host_princ, &_salt);
382
0
    if (ret) {
383
0
      DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
384
0
      return ret;
385
0
    }
386
0
  } else {
387
0
    _salt.saltvalue = *salt;
388
0
    _salt.salttype = KRB5_PW_SALT;
389
0
  }
390
391
0
  ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, _salt, key);
392
0
  if (salt == NULL) {
393
0
    krb5_free_salt(context, _salt);
394
0
  }
395
0
}
396
#else
397
#error UNKNOWN_CREATE_KEY_FUNCTIONS
398
#endif
399
0
  return ret;
400
0
}
401
402
/**
403
* @brief Create a salt for a given principal
404
*
405
* @param context  The initialized krb5_context
406
* @param host_princ The krb5_principal to create the salt for
407
* @param psalt    A pointer to a krb5_data struct
408
*
409
* caller has to free the contents of psalt with smb_krb5_free_data_contents
410
* when function has succeeded
411
*
412
* @return krb5_error_code, returns 0 on success, error code otherwise
413
*/
414
415
int smb_krb5_get_pw_salt(krb5_context context,
416
       krb5_const_principal host_princ,
417
       krb5_data *psalt)
418
#if defined(HAVE_KRB5_GET_PW_SALT)
419
/* Heimdal */
420
0
{
421
0
  int ret;
422
0
  krb5_salt salt;
423
424
0
  ret = krb5_get_pw_salt(context, host_princ, &salt);
425
0
  if (ret) {
426
0
    return ret;
427
0
  }
428
429
0
  *psalt = salt.saltvalue;
430
431
0
  return ret;
432
0
}
433
#elif defined(HAVE_KRB5_PRINCIPAL2SALT)
434
/* MIT */
435
{
436
  return krb5_principal2salt(context, host_princ, psalt);
437
}
438
#else
439
#error UNKNOWN_SALT_FUNCTIONS
440
#endif
441
442
/**
443
 * @brief This constructs the salt principal used by active directory
444
 *
445
 * Most Kerberos encryption types require a salt in order to
446
 * calculate the long term private key for user/computer object
447
 * based on a password.
448
 *
449
 * The returned _salt_principal is a string in forms like this:
450
 * - host/somehost.example.com@EXAMPLE.COM
451
 * - SomeAccount@EXAMPLE.COM
452
 * - SomePrincipal@EXAMPLE.COM
453
 *
454
 * This is not the form that's used as salt, it's just
455
 * the human readable form. It needs to be converted by
456
 * smb_krb5_salt_principal2data().
457
 *
458
 * @param[in]  realm              The realm the user/computer is added too.
459
 *
460
 * @param[in]  sAMAccountName     The sAMAccountName attribute of the object.
461
 *
462
 * @param[in]  userPrincipalName  The userPrincipalName attribute of the object
463
 *                                or NULL if not available.
464
 *
465
 * @param[in]  uac_flags          UF_ACCOUNT_TYPE_MASKed userAccountControl field
466
 *
467
 * @param[in]  mem_ctx            The TALLOC_CTX to allocate _salt_principal.
468
 *
469
 * @param[out]  _salt_principal   The resulting principal as string.
470
 *
471
 * @retval 0 Success; otherwise - Kerberos error codes
472
 *
473
 * @see smb_krb5_salt_principal2data
474
 */
475
int smb_krb5_salt_principal(krb5_context krb5_ctx,
476
          const char *realm,
477
          const char *sAMAccountName,
478
          const char *userPrincipalName,
479
          uint32_t uac_flags,
480
          krb5_principal *salt_princ)
481
0
{
482
0
  TALLOC_CTX *frame = talloc_stackframe();
483
0
  char *upper_realm = NULL;
484
0
  const char *principal = NULL;
485
0
  int principal_len = 0;
486
0
  krb5_error_code krb5_ret;
487
488
0
  *salt_princ = NULL;
489
490
0
  if (sAMAccountName == NULL) {
491
0
    TALLOC_FREE(frame);
492
0
    return EINVAL;
493
0
  }
494
495
0
  if (realm == NULL) {
496
0
    TALLOC_FREE(frame);
497
0
    return EINVAL;
498
0
  }
499
500
0
  if (uac_flags & ~UF_ACCOUNT_TYPE_MASK) {
501
    /*
502
     * catch callers which still
503
     * pass 'true'.
504
     */
505
0
    TALLOC_FREE(frame);
506
0
    return EINVAL;
507
0
  }
508
0
  if (uac_flags == 0) {
509
    /*
510
     * catch callers which still
511
     * pass 'false'.
512
     */
513
0
    TALLOC_FREE(frame);
514
0
    return EINVAL;
515
0
  }
516
517
0
  upper_realm = strupper_talloc(frame, realm);
518
0
  if (upper_realm == NULL) {
519
0
    TALLOC_FREE(frame);
520
0
    return ENOMEM;
521
0
  }
522
523
  /* Many, many thanks to lukeh@padl.com for this
524
   * algorithm, described in his Nov 10 2004 mail to
525
   * samba-technical@lists.samba.org */
526
527
  /*
528
   * Determine a salting principal
529
   */
530
0
  if (uac_flags & UF_TRUST_ACCOUNT_MASK) {
531
0
    int computer_len = 0;
532
533
0
    computer_len = strlen(sAMAccountName);
534
0
    if (sAMAccountName[computer_len-1] == '$') {
535
0
      computer_len -= 1;
536
0
    }
537
538
0
    if (uac_flags & UF_INTERDOMAIN_TRUST_ACCOUNT) {
539
0
      const char *krbtgt = "krbtgt";
540
0
      krb5_ret = krb5_build_principal_ext(krb5_ctx,
541
0
                  salt_princ,
542
0
                  strlen(upper_realm),
543
0
                  upper_realm,
544
0
                  strlen(krbtgt),
545
0
                  krbtgt,
546
0
                  computer_len,
547
0
                  sAMAccountName,
548
0
                  0);
549
0
      if (krb5_ret != 0) {
550
0
        TALLOC_FREE(frame);
551
0
        return krb5_ret;
552
0
      }
553
0
    } else {
554
0
      const char *host = "host";
555
0
      char *tmp = NULL;
556
0
      char *tmp_lower = NULL;
557
558
0
      tmp = talloc_asprintf(frame, "%*.*s.%s",
559
0
                computer_len,
560
0
                computer_len,
561
0
                sAMAccountName,
562
0
                realm);
563
0
      if (tmp == NULL) {
564
0
        TALLOC_FREE(frame);
565
0
        return ENOMEM;
566
0
      }
567
568
0
      tmp_lower = strlower_talloc(frame, tmp);
569
0
      if (tmp_lower == NULL) {
570
0
        TALLOC_FREE(frame);
571
0
        return ENOMEM;
572
0
      }
573
574
0
      krb5_ret = krb5_build_principal_ext(krb5_ctx,
575
0
                  salt_princ,
576
0
                  strlen(upper_realm),
577
0
                  upper_realm,
578
0
                  strlen(host),
579
0
                  host,
580
0
                  strlen(tmp_lower),
581
0
                  tmp_lower,
582
0
                  0);
583
0
      if (krb5_ret != 0) {
584
0
        TALLOC_FREE(frame);
585
0
        return krb5_ret;
586
0
      }
587
0
    }
588
589
0
  } else if (userPrincipalName != NULL) {
590
    /*
591
     * We parse the name not only to allow an easy
592
     * replacement of the realm (no matter the realm in
593
     * the UPN, the salt comes from the upper-case real
594
     * realm, but also to correctly provide a salt when
595
     * the UPN is host/foo.bar
596
     *
597
     * This can fail for a UPN of the form foo@bar@REALM
598
     * (which is accepted by windows) however.
599
     */
600
0
    krb5_ret = krb5_parse_name(krb5_ctx,
601
0
             userPrincipalName,
602
0
             salt_princ);
603
604
0
    if (krb5_ret != 0) {
605
0
      TALLOC_FREE(frame);
606
0
      return krb5_ret;
607
0
    }
608
609
    /*
610
     * No matter what realm (including none) in the UPN,
611
     * the realm is replaced with our upper-case realm
612
     */
613
0
    krb5_ret = smb_krb5_principal_set_realm(krb5_ctx,
614
0
              *salt_princ,
615
0
              upper_realm);
616
0
    if (krb5_ret != 0) {
617
0
      krb5_free_principal(krb5_ctx, *salt_princ);
618
0
      TALLOC_FREE(frame);
619
0
      return krb5_ret;
620
0
    }
621
0
  } else {
622
0
    principal = sAMAccountName;
623
0
    principal_len = strlen(principal);
624
625
0
    krb5_ret = krb5_build_principal_ext(krb5_ctx,
626
0
                salt_princ,
627
0
                strlen(upper_realm),
628
0
                upper_realm,
629
0
                principal_len,
630
0
                principal,
631
0
                0);
632
0
    if (krb5_ret != 0) {
633
0
      TALLOC_FREE(frame);
634
0
      return krb5_ret;
635
0
    }
636
0
  }
637
638
0
  TALLOC_FREE(frame);
639
0
  return 0;
640
0
}
641
642
/**
643
 * @brief This constructs the salt principal used by active directory
644
 *
645
 * Most Kerberos encryption types require a salt in order to
646
 * calculate the long term private key for user/computer object
647
 * based on a password.
648
 *
649
 * The returned _salt_principal is a string in forms like this:
650
 * - host/somehost.example.com@EXAMPLE.COM
651
 * - SomeAccount@EXAMPLE.COM
652
 * - SomePrincipal@EXAMPLE.COM
653
 *
654
 * This is not the form that's used as salt, it's just
655
 * the human readable form. It needs to be converted by
656
 * smb_krb5_salt_principal2data().
657
 *
658
 * @param[in]  realm              The realm the user/computer is added too.
659
 *
660
 * @param[in]  sAMAccountName     The sAMAccountName attribute of the object.
661
 *
662
 * @param[in]  userPrincipalName  The userPrincipalName attribute of the object
663
 *                                or NULL if not available.
664
 *
665
 * @param[in]  uac_flags          UF_ACCOUNT_TYPE_MASKed userAccountControl field
666
 *
667
 * @param[in]  mem_ctx            The TALLOC_CTX to allocate _salt_principal.
668
 *
669
 * @param[out]  _salt_principal   The resulting principal as string.
670
 *
671
 * @retval 0 Success; otherwise - Kerberos error codes
672
 *
673
 * @see smb_krb5_salt_principal2data
674
 */
675
int smb_krb5_salt_principal_str(const char *realm,
676
        const char *sAMAccountName,
677
        const char *userPrincipalName,
678
        uint32_t uac_flags,
679
        TALLOC_CTX *mem_ctx,
680
        char **_salt_principal_str)
681
0
{
682
0
  krb5_principal salt_principal = NULL;
683
0
  char *salt_principal_malloc;
684
0
  krb5_context krb5_ctx;
685
0
  krb5_error_code krb5_ret
686
0
    = smb_krb5_init_context_common(&krb5_ctx);
687
0
  if (krb5_ret != 0) {
688
0
    DBG_ERR("kerberos init context failed (%s)\n",
689
0
      error_message(krb5_ret));
690
0
    return krb5_ret;
691
0
  }
692
693
0
  krb5_ret = smb_krb5_salt_principal(krb5_ctx,
694
0
             realm,
695
0
             sAMAccountName,
696
0
             userPrincipalName,
697
0
             uac_flags,
698
0
             &salt_principal);
699
0
  if (krb5_ret != 0) {
700
0
    DBG_ERR("unable to create salt principal:%s\n",
701
0
      error_message(krb5_ret));
702
0
    return krb5_ret;
703
0
  }
704
705
0
  krb5_ret = krb5_unparse_name(krb5_ctx, salt_principal,
706
0
             &salt_principal_malloc);
707
0
  if (krb5_ret != 0) {
708
0
    krb5_free_principal(krb5_ctx, salt_principal);
709
0
    DBG_ERR("kerberos unparse of salt principal failed (%s)\n",
710
0
      error_message(krb5_ret));
711
0
    return krb5_ret;
712
0
  }
713
0
  krb5_free_principal(krb5_ctx, salt_principal);
714
0
  *_salt_principal_str
715
0
    = talloc_strdup(mem_ctx, salt_principal_malloc);
716
0
  krb5_free_unparsed_name(krb5_ctx, salt_principal_malloc);
717
718
0
  if (*_salt_principal_str == NULL) {
719
0
    return ENOMEM;
720
0
  }
721
0
  return 0;
722
0
}
723
724
/**
725
 * @brief Converts the salt principal string into the salt data blob
726
 *
727
 * This function takes a salt_principal as string in forms like this:
728
 * - host/somehost.example.com@EXAMPLE.COM
729
 * - SomeAccount@EXAMPLE.COM
730
 * - SomePrincipal@EXAMPLE.COM
731
 *
732
 * It generates values like:
733
 * - EXAMPLE.COMhost/somehost.example.com
734
 * - EXAMPLE.COMSomeAccount
735
 * - EXAMPLE.COMSomePrincipal
736
 *
737
 * @param[in]  realm              The realm the user/computer is added too.
738
 *
739
 * @param[in]  sAMAccountName     The sAMAccountName attribute of the object.
740
 *
741
 * @param[in]  userPrincipalName  The userPrincipalName attribute of the object
742
 *                                or NULL if not available.
743
 *
744
 * @param[in]  is_computer        The indication of the object includes
745
 *                                objectClass=computer.
746
 *
747
 * @param[in]  mem_ctx            The TALLOC_CTX to allocate _salt_principal.
748
 *
749
 * @param[out]  _salt_principal   The resulting principal as string.
750
 *
751
 * @retval 0 Success; otherwise - Kerberos error codes
752
 *
753
 * @see smb_krb5_salt_principal
754
 */
755
int smb_krb5_salt_principal2data(krb5_context context,
756
         const char *salt_principal,
757
         TALLOC_CTX *mem_ctx,
758
         char **_salt_data)
759
0
{
760
0
  krb5_error_code ret;
761
0
  krb5_principal salt_princ = NULL;
762
0
  krb5_data salt;
763
764
0
  *_salt_data = NULL;
765
766
0
  ret = krb5_parse_name(context, salt_principal, &salt_princ);
767
0
  if (ret != 0) {
768
0
    return ret;
769
0
  }
770
771
0
  ret = smb_krb5_get_pw_salt(context, salt_princ, &salt);
772
0
  krb5_free_principal(context, salt_princ);
773
0
  if (ret != 0) {
774
0
    return ret;
775
0
  }
776
777
0
  *_salt_data = talloc_strndup(mem_ctx,
778
0
             (char *)salt.data,
779
0
             salt.length);
780
0
  smb_krb5_free_data_contents(context, &salt);
781
0
  if (*_salt_data == NULL) {
782
0
    return ENOMEM;
783
0
  }
784
785
0
  return 0;
786
0
}
787
788
#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
789
/**
790
 * @brief Get a list of encryption types allowed for session keys
791
 *
792
 * @param[in]  context  The library context
793
 *
794
 * @param[in]  enctypes An allocated, zero-terminated list of encryption types
795
 *
796
 * This function returns an allocated list of encryption types allowed for
797
 * session keys.
798
 *
799
 * Use krb5_free_enctypes() to free the enctypes when it is no longer needed.
800
 *
801
 * @retval 0 Success; otherwise - Kerberos error codes
802
 */
803
krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context,
804
              krb5_enctype **enctypes)
805
{
806
  return krb5_get_permitted_enctypes(context, enctypes);
807
}
808
#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
809
krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context,
810
              krb5_enctype **enctypes)
811
0
{
812
0
#ifdef HAVE_KRB5_PDU_NONE_DECL
813
0
  return krb5_get_default_in_tkt_etypes(context, KRB5_PDU_NONE, enctypes);
814
#else
815
  return krb5_get_default_in_tkt_etypes(context, enctypes);
816
#endif
817
0
}
818
#else
819
#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
820
#endif
821
822
823
/**
824
 * @brief Convert a string principal name to a Kerberos principal.
825
 *
826
 * @param[in]  context  The library context
827
 *
828
 * @param[in]  name     The principal as a unix charset string.
829
 *
830
 * @param[out] principal The newly allocated principal.
831
 *
832
 * Use krb5_free_principal() to free a principal when it is no longer needed.
833
 *
834
 * @return 0 on success, a Kerberos error code otherwise.
835
 */
836
krb5_error_code smb_krb5_parse_name(krb5_context context,
837
            const char *name,
838
            krb5_principal *principal)
839
0
{
840
0
  return smb_krb5_parse_name_flags(context, name, 0, principal);
841
0
}
842
843
/**
844
 * @brief Convert a string principal name to a Kerberos principal.
845
 *
846
 * @param[in]  context  The library context
847
 *
848
 * @param[in]  name     The principal as a unix charset string.
849
 *
850
 * @param[in]  flags    Flags for krb5_parse_name_flags()
851
 *
852
 * @param[out] principal The newly allocated principal.
853
 *
854
 * Use krb5_free_principal() to free a principal when it is no longer needed.
855
 *
856
 * @return 0 on success, a Kerberos error code otherwise.
857
 */
858
krb5_error_code smb_krb5_parse_name_flags(krb5_context context,
859
            const char *name,
860
            int flags,
861
            krb5_principal *principal)
862
0
{
863
0
  krb5_error_code ret;
864
0
  char *utf8_name;
865
0
  size_t converted_size;
866
0
  TALLOC_CTX *frame = talloc_stackframe();
867
868
0
  if (!push_utf8_talloc(frame, &utf8_name, name, &converted_size)) {
869
0
    TALLOC_FREE(frame);
870
0
    return ENOMEM;
871
0
  }
872
873
0
  ret = krb5_parse_name_flags(context, utf8_name, flags, principal);
874
0
  if (ret != KRB5_PARSE_MALFORMED) {
875
0
    TALLOC_FREE(frame);
876
0
    return ret;
877
0
  }
878
879
0
  flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
880
0
  ret = krb5_parse_name_flags(context, utf8_name, flags, principal);
881
882
0
  TALLOC_FREE(frame);
883
0
  return ret;
884
0
}
885
886
/**
887
 * @brief Convert a Kerberos principal structure to a string representation.
888
 *
889
 * The resulting string representation will be a unix charset name and is
890
 * talloc'ed.
891
 *
892
 * @param[in]  mem_ctx  The talloc context to allocate memory on.
893
 *
894
 * @param[in]  context  The library context.
895
 *
896
 * @param[in]  principal The principal.
897
 *
898
 * @param[out] unix_name A string representation of the principal name as with
899
 *                       unix charset.
900
 *
901
 * Use talloc_free() to free the string representation if it is no longer
902
 * needed.
903
 *
904
 * @return 0 on success, a Kerberos error code otherwise.
905
 */
906
krb5_error_code smb_krb5_unparse_name(TALLOC_CTX *mem_ctx,
907
              krb5_context context,
908
              krb5_const_principal principal,
909
              char **unix_name)
910
0
{
911
0
  krb5_error_code ret;
912
0
  char *utf8_name;
913
0
  size_t converted_size;
914
915
0
  *unix_name = NULL;
916
0
  ret = krb5_unparse_name(context, principal, &utf8_name);
917
0
  if (ret) {
918
0
    return ret;
919
0
  }
920
921
0
  if (!pull_utf8_talloc(mem_ctx, unix_name, utf8_name, &converted_size)) {
922
0
    krb5_free_unparsed_name(context, utf8_name);
923
0
    return ENOMEM;
924
0
  }
925
0
  krb5_free_unparsed_name(context, utf8_name);
926
0
  return 0;
927
0
}
928
929
/**
930
 * @brief Free the contents of a krb5_data structure and zero the data field.
931
 *
932
 * @param[in]  context  The krb5 context
933
 *
934
 * @param[in]  pdata    The data structure to free contents of
935
 *
936
 * This function frees the contents, not the structure itself.
937
 */
938
void smb_krb5_free_data_contents(krb5_context context, krb5_data *pdata)
939
0
{
940
#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
941
  if (pdata->data) {
942
    krb5_free_data_contents(context, pdata);
943
  }
944
#elif defined(HAVE_KRB5_DATA_FREE)
945
  krb5_data_free(context, pdata);
946
#else
947
0
  SAFE_FREE(pdata->data);
948
0
#endif
949
0
}
950
951
/*
952
 * @brief copy a buffer into a krb5_data struct
953
 *
954
 * @param[in] p     The krb5_data
955
 * @param[in] data    The data to copy
956
 * @param[in] length    The length of the data to copy
957
 * @return krb5_error_code
958
 *
959
 * Caller has to free krb5_data with smb_krb5_free_data_contents().
960
 */
961
krb5_error_code smb_krb5_copy_data_contents(krb5_data *p,
962
              const void *data,
963
              size_t len)
964
0
{
965
0
#if defined(HAVE_KRB5_DATA_COPY)
966
0
  return krb5_data_copy(p, data, len);
967
#else
968
  if (len) {
969
    p->data = malloc(len);
970
    if (p->data == NULL) {
971
      return ENOMEM;
972
    }
973
    memmove(p->data, data, len);
974
  } else {
975
    p->data = NULL;
976
  }
977
  p->length = len;
978
  p->magic = KV5M_DATA;
979
  return 0;
980
#endif
981
0
}
982
983
/*
984
 * @brief put a buffer reference into a krb5_data struct
985
 *
986
 * @param[in] data    The data to reference
987
 * @param[in] length    The length of the data to reference
988
 * @return krb5_data
989
 *
990
 * Caller should not free krb5_data.
991
 */
992
krb5_data smb_krb5_make_data(void *data,
993
           size_t len)
994
0
{
995
0
  krb5_data d;
996
997
0
#ifdef SAMBA4_USES_HEIMDAL
998
0
  d.data = (uint8_t *)data;
999
0
  d.length = len;
1000
#else
1001
  d.magic = KV5M_DATA;
1002
  d.data = data;
1003
  d.length = len;
1004
#endif
1005
0
  return d;
1006
0
}
1007
1008
krb5_data smb_krb5_data_from_blob(DATA_BLOB blob)
1009
0
{
1010
0
  return smb_krb5_make_data(blob.data, blob.length);
1011
0
}
1012
1013
bool smb_krb5_get_smb_session_key(TALLOC_CTX *mem_ctx,
1014
          krb5_context context,
1015
          krb5_auth_context auth_context,
1016
          DATA_BLOB *session_key,
1017
          bool remote)
1018
0
{
1019
0
  krb5_keyblock *skey = NULL;
1020
0
  krb5_error_code err = 0;
1021
0
  bool ret = false;
1022
1023
0
  if (remote) {
1024
#ifdef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
1025
    err = krb5_auth_con_getrecvsubkey(context,
1026
              auth_context,
1027
              &skey);
1028
#else /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
1029
0
    err = krb5_auth_con_getremotesubkey(context,
1030
0
                auth_context, &skey);
1031
0
#endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
1032
0
  } else {
1033
#ifdef HAVE_KRB5_AUTH_CON_GETSENDSUBKEY
1034
    err = krb5_auth_con_getsendsubkey(context,
1035
              auth_context,
1036
              &skey);
1037
#else /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
1038
0
    err = krb5_auth_con_getlocalsubkey(context,
1039
0
               auth_context, &skey);
1040
0
#endif /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
1041
0
  }
1042
1043
0
  if (err || skey == NULL) {
1044
0
    DEBUG(10, ("KRB5 error getting session key %d\n", err));
1045
0
    goto done;
1046
0
  }
1047
1048
0
  DEBUG(10, ("Got KRB5 session key of length %d\n",
1049
0
       (int)KRB5_KEY_LENGTH(skey)));
1050
1051
0
  *session_key = data_blob_talloc_s(mem_ctx,
1052
0
            KRB5_KEY_DATA(skey),
1053
0
            KRB5_KEY_LENGTH(skey));
1054
0
  if (session_key->data == NULL) {
1055
0
    DBG_WARNING("No memory for session key\n");
1056
0
    goto done;
1057
0
  }
1058
0
  dump_data_pw("KRB5 Session Key:\n",
1059
0
         session_key->data,
1060
0
         session_key->length);
1061
1062
0
  ret = true;
1063
1064
0
done:
1065
0
  if (skey) {
1066
0
    krb5_free_keyblock(context, skey);
1067
0
  }
1068
1069
0
  return ret;
1070
0
}
1071
1072
1073
/**
1074
 * @brief Get talloced string component of a principal
1075
 *
1076
 * @param[in] mem_ctx   The TALLOC_CTX
1077
 * @param[in] context   The krb5_context
1078
 * @param[in] principal   The principal
1079
 * @param[in] component   The component
1080
 * @param[out] out      The output string
1081
 * @return krb5_error_code
1082
 *
1083
 * Caller must talloc_free if the return value is not NULL.
1084
 *
1085
 */
1086
krb5_error_code smb_krb5_principal_get_comp_string(TALLOC_CTX *mem_ctx,
1087
               krb5_context context,
1088
               krb5_const_principal principal,
1089
               unsigned int component,
1090
               char **out)
1091
0
{
1092
0
  char *out_str = NULL;
1093
0
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
1094
0
  const char *str = NULL;
1095
1096
0
  str = krb5_principal_get_comp_string(context, principal, component);
1097
0
  if (str == NULL) {
1098
0
    return ENOENT;
1099
0
  }
1100
1101
0
  out_str = talloc_strdup(mem_ctx, str);
1102
0
  if (out_str == NULL) {
1103
0
    return ENOMEM;
1104
0
  }
1105
#else
1106
  krb5_data *data;
1107
1108
  if (component >= krb5_princ_size(context, principal)) {
1109
    return ENOENT;
1110
  }
1111
1112
  data = krb5_princ_component(context, principal, component);
1113
  if (data == NULL) {
1114
    return ENOENT;
1115
  }
1116
1117
  out_str = talloc_strndup(mem_ctx, data->data, data->length);
1118
  if (out_str == NULL) {
1119
    return ENOMEM;
1120
  }
1121
#endif
1122
0
  *out = out_str;
1123
0
  return 0;
1124
0
}
1125
1126
krb5_error_code smb_krb5_cc_new_unique_memory(krb5_context context,
1127
                TALLOC_CTX *mem_ctx,
1128
                char **ccache_name,
1129
                krb5_ccache *id)
1130
0
{
1131
0
  krb5_error_code code;
1132
0
  const char *type = NULL;
1133
0
  const char *name = NULL;
1134
1135
0
  if (ccache_name != NULL) {
1136
0
    *ccache_name = NULL;
1137
0
  }
1138
0
  *id = NULL;
1139
1140
0
#ifdef SAMBA4_USES_HEIMDAL
1141
  /*
1142
   * "MEMORY:anonymous" is not visible to
1143
   * the credential cache collection iterator
1144
   *
1145
   * It creates anonymous-POINTER-UNIQUECOUNTTER
1146
   * in the background.
1147
   */
1148
0
  code = krb5_cc_resolve(context, "MEMORY:anonymous", id);
1149
0
  if (code != 0) {
1150
0
    DBG_ERR("krb5_cc_resolve(MEMORY:anonymous) failed: %s\n",
1151
0
      smb_get_krb5_error_message(
1152
0
        context, code, mem_ctx));
1153
0
    return code;
1154
0
  }
1155
#else /* MIT */
1156
  /*
1157
   * In MIT the "MEMORY:" credential cache collection
1158
   * only contains the default cache (at most).
1159
   */
1160
  code = krb5_cc_new_unique(context, "MEMORY", NULL, id);
1161
  if (code != 0) {
1162
    DBG_ERR("krb5_cc_new_unique failed: %s\n",
1163
      smb_get_krb5_error_message(
1164
        context, code, mem_ctx));
1165
    return code;
1166
  }
1167
#endif /* MIT */
1168
1169
0
  type = krb5_cc_get_type(context, *id);
1170
0
  if (type == NULL) {
1171
0
    DBG_ERR("krb5_cc_get_type failed...\n");
1172
0
    krb5_cc_destroy(context, *id);
1173
0
    *id = NULL;
1174
0
    return KRB5_CC_UNKNOWN_TYPE;
1175
0
  }
1176
1177
0
  name = krb5_cc_get_name(context, *id);
1178
0
  if (name == NULL) {
1179
0
    DBG_ERR("krb5_cc_get_name failed...\n");
1180
0
    krb5_cc_destroy(context, *id);
1181
0
    *id = NULL;
1182
0
    return KRB5_CC_BADNAME;
1183
0
  }
1184
1185
0
  if (ccache_name == NULL) {
1186
0
    return 0;
1187
0
  }
1188
1189
0
  *ccache_name = talloc_asprintf(mem_ctx, "%s:%s", type, name);
1190
0
  if (*ccache_name == NULL) {
1191
0
    DBG_ERR("krb5_cc_get_name failed...\n");
1192
0
    krb5_cc_destroy(context, *id);
1193
0
    *id = NULL;
1194
0
    return ENOMEM;
1195
0
  }
1196
1197
0
  return 0;
1198
0
}
1199
1200
/**
1201
 * @brief
1202
 *
1203
 * @param[in]  ccache_string A string pointing to the cache to renew the ticket
1204
 *                           (e.g. FILE:/tmp/krb5cc_0) or NULL. If the principal
1205
 *                           ccache has not been specified, the default ccache
1206
 *                           will be used.
1207
 *
1208
 * @param[in]  client_string The client principal string (e.g. user@SAMBA.SITE)
1209
 *                           or NULL. If the principal string has not been
1210
 *                           specified, the principal from the ccache will be
1211
 *                           retrieved.
1212
 *
1213
 * @param[in]  service_string The service ticket string
1214
 *                            (e.g. krbtgt/SAMBA.SITE@SAMBA.SITE) or NULL. If
1215
 *                            the service ticket is specified, it is parsed
1216
 *                            (with the realm part ignored) and used as the
1217
 *                            server principal of the credential. Otherwise
1218
 *                            the ticket-granting service is used.
1219
 *
1220
 * @param[in]  expire_time    A pointer to store the credentials end time or
1221
 *                            NULL.
1222
 *
1223
 * @return 0 on Success, a Kerberos error code otherwise.
1224
 */
1225
krb5_error_code smb_krb5_renew_ticket(const char *ccache_string,
1226
              const char *client_string,
1227
              const char *service_string,
1228
              time_t *expire_time)
1229
0
{
1230
0
  krb5_error_code ret;
1231
0
  krb5_context context = NULL;
1232
0
  krb5_ccache ccache = NULL;
1233
0
  krb5_principal client = NULL;
1234
0
  krb5_creds creds, creds_in;
1235
1236
0
  ZERO_STRUCT(creds);
1237
0
  ZERO_STRUCT(creds_in);
1238
1239
0
  ret = smb_krb5_init_context_common(&context);
1240
0
  if (ret) {
1241
0
    DBG_ERR("kerberos init context failed (%s)\n",
1242
0
      error_message(ret));
1243
0
    goto done;
1244
0
  }
1245
1246
0
  if (!ccache_string) {
1247
    /*
1248
     * A renew has low risk in mixing different
1249
     * ccaches, so we allow callers to pass
1250
     * NULL for the default cache.
1251
     */
1252
0
    ccache_string = smb_force_krb5_cc_default_name(context);
1253
0
  }
1254
1255
0
  if (!ccache_string) {
1256
0
    ret = EINVAL;
1257
0
    goto done;
1258
0
  }
1259
1260
0
  DBG_DEBUG("Using %s as ccache for client '%s' and service '%s'\n",
1261
0
      ccache_string, client_string, service_string);
1262
1263
  /* FIXME: we should not fall back to defaults */
1264
0
  ret = krb5_cc_resolve(context, discard_const_p(char, ccache_string), &ccache);
1265
0
  if (ret) {
1266
0
    goto done;
1267
0
  }
1268
1269
0
  if (client_string) {
1270
0
    ret = smb_krb5_parse_name(context, client_string, &client);
1271
0
    if (ret) {
1272
0
      goto done;
1273
0
    }
1274
0
  } else {
1275
0
    ret = krb5_cc_get_principal(context, ccache, &client);
1276
0
    if (ret) {
1277
0
      goto done;
1278
0
    }
1279
0
  }
1280
1281
0
  ret = krb5_get_renewed_creds(context, &creds, client, ccache, discard_const_p(char, service_string));
1282
0
  if (ret) {
1283
0
    DBG_DEBUG("krb5_get_renewed_creds using ccache '%s' "
1284
0
        "for client '%s' and service '%s' failed: %s\n",
1285
0
        ccache_string, client_string, service_string,
1286
0
        error_message(ret));
1287
0
    goto done;
1288
0
  }
1289
1290
  /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
1291
0
  ret = krb5_cc_initialize(context, ccache, client);
1292
0
  if (ret) {
1293
0
    goto done;
1294
0
  }
1295
1296
0
  ret = krb5_cc_store_cred(context, ccache, &creds);
1297
1298
0
  if (expire_time) {
1299
0
    *expire_time = (time_t) creds.times.endtime;
1300
0
  }
1301
1302
0
done:
1303
0
  krb5_free_cred_contents(context, &creds_in);
1304
0
  krb5_free_cred_contents(context, &creds);
1305
1306
0
  if (client) {
1307
0
    krb5_free_principal(context, client);
1308
0
  }
1309
0
  if (ccache) {
1310
0
    krb5_cc_close(context, ccache);
1311
0
  }
1312
0
  if (context) {
1313
0
    krb5_free_context(context);
1314
0
  }
1315
1316
0
  return ret;
1317
0
}
1318
1319
/**
1320
 * @brief Free the data stored in an smb_krb5_addresses structure.
1321
 *
1322
 * @param[in]  context  The library context
1323
 *
1324
 * @param[in]  addr     The address structure to free.
1325
 *
1326
 * @return 0 on success, a Kerberos error code otherwise.
1327
 */
1328
krb5_error_code smb_krb5_free_addresses(krb5_context context,
1329
          smb_krb5_addresses *addr)
1330
0
{
1331
0
  krb5_error_code ret = 0;
1332
0
  if (addr == NULL) {
1333
0
    return ret;
1334
0
  }
1335
#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1336
  krb5_free_addresses(context, addr->addrs);
1337
#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1338
0
  ret = krb5_free_addresses(context, addr->addrs);
1339
0
  SAFE_FREE(addr->addrs);
1340
0
#endif
1341
0
  SAFE_FREE(addr);
1342
0
  addr = NULL;
1343
0
  return ret;
1344
0
}
1345
1346
0
#define MAX_NETBIOSNAME_LEN 16
1347
1348
/**
1349
 * @brief Add a netbios name to the array of addresses
1350
 *
1351
 * @param[in]  kerb_addr A pointer to the smb_krb5_addresses to add the
1352
 *                       netbios name to.
1353
 *
1354
 * @param[in]  netbios_name The netbios name to add.
1355
 *
1356
 * @return 0 on success, a Kerberos error code otherwise.
1357
 */
1358
krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr,
1359
               const char *netbios_name)
1360
0
{
1361
0
  krb5_error_code ret = 0;
1362
0
  char buf[MAX_NETBIOSNAME_LEN];
1363
0
  int len;
1364
#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1365
  krb5_address **addrs = NULL;
1366
#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1367
0
  krb5_addresses *addrs = NULL;
1368
0
#endif
1369
1370
0
  *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
1371
0
  if (*kerb_addr == NULL) {
1372
0
    return ENOMEM;
1373
0
  }
1374
1375
  /* temporarily duplicate put_name() code here to avoid dependency
1376
   * issues for a 5 lines function */
1377
0
  len = strlen(netbios_name);
1378
0
  memcpy(buf, netbios_name,
1379
0
    (len < MAX_NETBIOSNAME_LEN) ? len : MAX_NETBIOSNAME_LEN - 1);
1380
0
  if (len < MAX_NETBIOSNAME_LEN - 1) {
1381
0
    memset(buf + len, ' ', MAX_NETBIOSNAME_LEN - 1 - len);
1382
0
  }
1383
0
  buf[MAX_NETBIOSNAME_LEN - 1] = 0x20;
1384
1385
#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1386
  {
1387
    int num_addr = 2;
1388
1389
    addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
1390
    if (addrs == NULL) {
1391
      SAFE_FREE(*kerb_addr);
1392
      return ENOMEM;
1393
    }
1394
1395
    memset(addrs, 0, sizeof(krb5_address *) * num_addr);
1396
1397
    addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1398
    if (addrs[0] == NULL) {
1399
      SAFE_FREE(addrs);
1400
      SAFE_FREE(*kerb_addr);
1401
      return ENOMEM;
1402
    }
1403
1404
    addrs[0]->magic = KV5M_ADDRESS;
1405
    addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
1406
    addrs[0]->length = MAX_NETBIOSNAME_LEN;
1407
    addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
1408
    if (addrs[0]->contents == NULL) {
1409
      SAFE_FREE(addrs[0]);
1410
      SAFE_FREE(addrs);
1411
      SAFE_FREE(*kerb_addr);
1412
      return ENOMEM;
1413
    }
1414
1415
    memcpy(addrs[0]->contents, buf, addrs[0]->length);
1416
1417
    addrs[1] = NULL;
1418
  }
1419
#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1420
  {
1421
0
    addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
1422
0
    if (addrs == NULL) {
1423
0
      SAFE_FREE(*kerb_addr);
1424
0
      return ENOMEM;
1425
0
    }
1426
1427
0
    memset(addrs, 0, sizeof(krb5_addresses));
1428
1429
0
    addrs->len = 1;
1430
0
    addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1431
0
    if (addrs->val == NULL) {
1432
0
      SAFE_FREE(addrs);
1433
0
      SAFE_FREE(*kerb_addr);
1434
0
      return ENOMEM;
1435
0
    }
1436
1437
0
    addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
1438
0
    addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
1439
0
    addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
1440
0
    if (addrs->val[0].address.data == NULL) {
1441
0
      SAFE_FREE(addrs->val);
1442
0
      SAFE_FREE(addrs);
1443
0
      SAFE_FREE(*kerb_addr);
1444
0
      return ENOMEM;
1445
0
    }
1446
1447
0
    memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
1448
0
  }
1449
#else
1450
#error UNKNOWN_KRB5_ADDRESS_FORMAT
1451
#endif
1452
0
  (*kerb_addr)->addrs = addrs;
1453
1454
0
  return ret;
1455
0
}
1456
1457
/**
1458
 * @brief Get the enctype from a key table entry
1459
 *
1460
 * @param[in]  kt_entry Key table entry to get the enctype from.
1461
 *
1462
 * @return The enctype from the entry.
1463
 */
1464
krb5_enctype smb_krb5_kt_get_enctype_from_entry(krb5_keytab_entry *kt_entry)
1465
0
{
1466
0
  return KRB5_KEY_TYPE(KRB5_KT_KEY(kt_entry));
1467
0
}
1468
1469
/**
1470
 * @brief Free the contents of a key table entry.
1471
 *
1472
 * @param[in]  context The library context.
1473
 *
1474
 * @param[in]  kt_entry The key table entry to free the contents of.
1475
 *
1476
 * @return 0 on success, a Kerberos error code otherwise.
1477
 *
1478
 * The pointer itself is not freed.
1479
 */
1480
krb5_error_code smb_krb5_kt_free_entry(krb5_context context,
1481
          krb5_keytab_entry *kt_entry)
1482
0
{
1483
/* Try krb5_free_keytab_entry_contents first, since
1484
 * MIT Kerberos >= 1.7 has both krb5_free_keytab_entry_contents and
1485
 * krb5_kt_free_entry but only has a prototype for the first, while the
1486
 * second is considered private.
1487
 */
1488
#if defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
1489
  return krb5_free_keytab_entry_contents(context, kt_entry);
1490
#elif defined(HAVE_KRB5_KT_FREE_ENTRY)
1491
  return krb5_kt_free_entry(context, kt_entry);
1492
#else
1493
#error UNKNOWN_KT_FREE_FUNCTION
1494
#endif
1495
0
}
1496
1497
1498
/**
1499
 * @brief Convert an encryption type to a string.
1500
 *
1501
 * @param[in]  context The library context.
1502
 *
1503
 * @param[in]  enctype The encryption type.
1504
 *
1505
 * @param[in]  etype_s A pointer to store the allocated encryption type as a
1506
 *                     string.
1507
 *
1508
 * @return 0 on success, a Kerberos error code otherwise.
1509
 *
1510
 * The caller needs to free the allocated string etype_s.
1511
 */
1512
krb5_error_code smb_krb5_enctype_to_string(krb5_context context,
1513
             krb5_enctype enctype,
1514
             char **etype_s)
1515
0
{
1516
0
#ifdef HAVE_KRB5_ENCTYPE_TO_STRING_WITH_KRB5_CONTEXT_ARG
1517
0
  return krb5_enctype_to_string(context, enctype, etype_s); /* Heimdal */
1518
#elif defined(HAVE_KRB5_ENCTYPE_TO_STRING_WITH_SIZE_T_ARG)
1519
  char buf[256];
1520
  krb5_error_code ret = krb5_enctype_to_string(enctype, buf, 256); /* MIT */
1521
  if (ret) {
1522
    return ret;
1523
  }
1524
  *etype_s = SMB_STRDUP(buf);
1525
  if (!*etype_s) {
1526
    return ENOMEM;
1527
  }
1528
  return ret;
1529
#else
1530
#error UNKNOWN_KRB5_ENCTYPE_TO_STRING_FUNCTION
1531
#endif
1532
0
}
1533
1534
/* This MAX_NAME_LEN is a constant defined in krb5.h */
1535
#ifndef MAX_KEYTAB_NAME_LEN
1536
0
#define MAX_KEYTAB_NAME_LEN 1100
1537
#endif
1538
1539
/**
1540
 * @brief Open a key table readonly or with readwrite access.
1541
 *
1542
 * Allows one to use a different keytab than the default one using a relative
1543
 * path to the keytab.
1544
 *
1545
 * @param[in]  context  The library context
1546
 *
1547
 * @param[in]  keytab_name_req The path to the key table.
1548
 *
1549
 * @param[in]  write_access Open with readwrite access.
1550
 *
1551
 * @param[in]  keytab A pointer to the opened key table.
1552
 *
1553
 * The keytab pointer should be freed using krb5_kt_close().
1554
 *
1555
 * @return 0 on success, a Kerberos error code otherwise.
1556
 */
1557
krb5_error_code smb_krb5_kt_open_relative(krb5_context context,
1558
            const char *keytab_name_req,
1559
            bool write_access,
1560
            krb5_keytab *keytab)
1561
0
{
1562
0
  krb5_error_code ret = 0;
1563
0
  TALLOC_CTX *mem_ctx;
1564
0
  char keytab_string[MAX_KEYTAB_NAME_LEN];
1565
0
  char *kt_str = NULL;
1566
0
  bool found_valid_name = false;
1567
0
  const char *pragma = "FILE";
1568
0
  const char *tmp = NULL;
1569
1570
0
  if (!write_access && !keytab_name_req) {
1571
    /* caller just wants to read the default keytab readonly, so be it */
1572
0
    return krb5_kt_default(context, keytab);
1573
0
  }
1574
1575
0
  mem_ctx = talloc_init("smb_krb5_kt_open_relative");
1576
0
  if (!mem_ctx) {
1577
0
    return ENOMEM;
1578
0
  }
1579
1580
#ifdef HAVE_WRFILE_KEYTAB
1581
  if (write_access) {
1582
    pragma = "WRFILE";
1583
  }
1584
#endif
1585
1586
0
  if (keytab_name_req) {
1587
1588
0
    if (strlen(keytab_name_req) > MAX_KEYTAB_NAME_LEN) {
1589
0
      ret = KRB5_CONFIG_NOTENUFSPACE;
1590
0
      goto out;
1591
0
    }
1592
1593
0
    if ((strncmp(keytab_name_req, "WRFILE:", 7) == 0) ||
1594
0
        (strncmp(keytab_name_req, "FILE:", 5) == 0)) {
1595
0
      tmp = keytab_name_req;
1596
0
      goto resolve;
1597
0
    }
1598
1599
0
    tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, keytab_name_req);
1600
0
    if (!tmp) {
1601
0
      ret = ENOMEM;
1602
0
      goto out;
1603
0
    }
1604
1605
0
    goto resolve;
1606
0
  }
1607
1608
  /* we need to handle more complex keytab_strings, like:
1609
   * "ANY:FILE:/etc/krb5.keytab,krb4:/etc/srvtab" */
1610
1611
0
  ret = krb5_kt_default_name(context, &keytab_string[0], MAX_KEYTAB_NAME_LEN - 2);
1612
0
  if (ret) {
1613
0
    goto out;
1614
0
  }
1615
1616
0
  DBG_DEBUG("krb5_kt_default_name returned %s\n", keytab_string);
1617
1618
0
  tmp = talloc_strdup(mem_ctx, keytab_string);
1619
0
  if (!tmp) {
1620
0
    ret = ENOMEM;
1621
0
    goto out;
1622
0
  }
1623
1624
0
  if (strncmp(tmp, "ANY:", 4) == 0) {
1625
0
    tmp += 4;
1626
0
  }
1627
1628
0
  memset(&keytab_string, '\0', sizeof(keytab_string));
1629
1630
0
  while (next_token_talloc(mem_ctx, &tmp, &kt_str, ",")) {
1631
0
    if (strncmp(kt_str, "WRFILE:", 7) == 0) {
1632
0
      found_valid_name = true;
1633
0
      tmp = kt_str;
1634
0
      tmp += 7;
1635
0
    }
1636
1637
0
    if (strncmp(kt_str, "FILE:", 5) == 0) {
1638
0
      found_valid_name = true;
1639
0
      tmp = kt_str;
1640
0
      tmp += 5;
1641
0
    }
1642
1643
0
    if (tmp[0] == '/') {
1644
      /* Treat as a FILE: keytab definition. */
1645
0
      found_valid_name = true;
1646
0
    }
1647
1648
0
    if (found_valid_name) {
1649
0
      if (tmp[0] != '/') {
1650
0
        ret = KRB5_KT_BADNAME;
1651
0
        goto out;
1652
0
      }
1653
1654
0
      tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, tmp);
1655
0
      if (!tmp) {
1656
0
        ret = ENOMEM;
1657
0
        goto out;
1658
0
      }
1659
0
      break;
1660
0
    }
1661
0
  }
1662
1663
0
  if (!found_valid_name) {
1664
0
    ret = KRB5_KT_UNKNOWN_TYPE;
1665
0
    goto out;
1666
0
  }
1667
1668
0
resolve:
1669
0
  DBG_DEBUG("resolving: %s\n", tmp);
1670
0
  ret = krb5_kt_resolve(context, tmp, keytab);
1671
1672
0
out:
1673
0
  TALLOC_FREE(mem_ctx);
1674
0
  return ret;
1675
0
}
1676
1677
/**
1678
 * @brief Open a key table readonly or with readwrite access.
1679
 *
1680
 * Allows one to use a different keytab than the default one. The path needs to be
1681
 * an absolute path or an error will be returned.
1682
 *
1683
 * @param[in]  context  The library context
1684
 *
1685
 * @param[in]  keytab_name_req The path to the key table.
1686
 *
1687
 * @param[in]  write_access Open with readwrite access.
1688
 *
1689
 * @param[in]  keytab A pointer to the opened key table.
1690
 *
1691
 * The keytab pointer should be freed using krb5_kt_close().
1692
 *
1693
 * @return 0 on success, a Kerberos error code otherwise.
1694
 */
1695
krb5_error_code smb_krb5_kt_open(krb5_context context,
1696
         const char *keytab_name_req,
1697
         bool write_access,
1698
         krb5_keytab *keytab)
1699
0
{
1700
0
  int cmp;
1701
1702
0
  if (keytab_name_req == NULL) {
1703
0
    return KRB5_KT_BADNAME;
1704
0
  }
1705
1706
0
  if (keytab_name_req[0] == '/') {
1707
0
    goto open_keytab;
1708
0
  }
1709
1710
0
  cmp = strncmp(keytab_name_req, "FILE:/", 6);
1711
0
  if (cmp == 0) {
1712
0
    goto open_keytab;
1713
0
  }
1714
1715
0
  cmp = strncmp(keytab_name_req, "WRFILE:/", 8);
1716
0
  if (cmp == 0) {
1717
0
    goto open_keytab;
1718
0
  }
1719
1720
0
  DBG_WARNING("ERROR: Invalid keytab name: %s\n", keytab_name_req);
1721
1722
0
  return KRB5_KT_BADNAME;
1723
1724
0
open_keytab:
1725
0
  return smb_krb5_kt_open_relative(context,
1726
0
           keytab_name_req,
1727
0
           write_access,
1728
0
           keytab);
1729
0
}
1730
1731
/**
1732
 * @brief Get a key table name.
1733
 *
1734
 * @param[in]  mem_ctx The talloc context to use for allocation.
1735
 *
1736
 * @param[in]  context The library context.
1737
 *
1738
 * @param[in]  keytab The key table to get the name from.
1739
 *
1740
 * @param[in]  keytab_name A talloc'ed string of the key table name.
1741
 *
1742
 * The talloc'ed name string needs to be freed with talloc_free().
1743
 *
1744
 * @return 0 on success, a Kerberos error code otherwise.
1745
 */
1746
krb5_error_code smb_krb5_kt_get_name(TALLOC_CTX *mem_ctx,
1747
             krb5_context context,
1748
             krb5_keytab keytab,
1749
             const char **keytab_name)
1750
0
{
1751
0
  char keytab_string[MAX_KEYTAB_NAME_LEN];
1752
0
  krb5_error_code ret = 0;
1753
1754
0
  ret = krb5_kt_get_name(context, keytab,
1755
0
             keytab_string, MAX_KEYTAB_NAME_LEN - 2);
1756
0
  if (ret) {
1757
0
    return ret;
1758
0
  }
1759
1760
0
  *keytab_name = talloc_strdup(mem_ctx, keytab_string);
1761
0
  if (!*keytab_name) {
1762
0
    return ENOMEM;
1763
0
  }
1764
1765
0
  return ret;
1766
0
}
1767
1768
/**
1769
 * @brief Seek and delete old entries in a keytab based on the passed
1770
 *        principal.
1771
 *
1772
 * @param[in]  context       The KRB5 context to use.
1773
 *
1774
 * @param[in]  keytab        The keytab to operate on.
1775
 *
1776
 * @param[in]  keep_old_kvno Keep the entries with the previous kvno.
1777
 *
1778
 * @param[in]  kvno          The kvno to use.
1779
 *
1780
 * @param[in]  enctype_only  Only evaluate the enctype argument if true
1781
 *
1782
 * @param[in]  enctype       Only search for entries with the specified enctype
1783
 *
1784
 * @param[in]  princ_s       The principal as a string to search for.
1785
 *
1786
 * @param[in]  princ         The principal as a krb5_principal to search for.
1787
 *
1788
 * @param[in]  flush         Whether to flush the complete keytab.
1789
 *
1790
 * @retval 0 on Success
1791
 *
1792
 * @return An appropriate KRB5 error code.
1793
 */
1794
krb5_error_code smb_krb5_kt_seek_and_delete_old_entries(krb5_context context,
1795
              krb5_keytab keytab,
1796
              bool keep_old_kvno,
1797
              krb5_kvno kvno,
1798
              bool enctype_only,
1799
              krb5_enctype enctype,
1800
              const char *princ_s,
1801
              krb5_principal princ,
1802
              bool flush)
1803
0
{
1804
0
  krb5_error_code ret;
1805
0
  krb5_kt_cursor cursor;
1806
0
  krb5_keytab_entry kt_entry;
1807
0
  char *ktprinc = NULL;
1808
0
  krb5_kvno old_kvno = kvno - 1;
1809
0
  TALLOC_CTX *tmp_ctx;
1810
1811
0
  if (flush) {
1812
0
    SMB_ASSERT(!keep_old_kvno);
1813
0
    SMB_ASSERT(!enctype_only);
1814
0
    SMB_ASSERT(princ_s == NULL);
1815
0
    SMB_ASSERT(princ == NULL);
1816
0
  } else {
1817
0
    SMB_ASSERT(princ_s != NULL);
1818
0
    SMB_ASSERT(princ != NULL);
1819
0
  }
1820
1821
0
  ZERO_STRUCT(cursor);
1822
0
  ZERO_STRUCT(kt_entry);
1823
1824
  /*
1825
   * Start with talloc_new() and only then call krb5_kt_start_seq_get().
1826
   * If any of them fails, the cleanup code is simpler.
1827
   */
1828
0
  tmp_ctx = talloc_new(NULL);
1829
0
  if (tmp_ctx == NULL) {
1830
0
    return ENOMEM;
1831
0
  }
1832
1833
0
  ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1834
0
  if (ret == KRB5_KT_END || ret == ENOENT ) {
1835
    /* no entries */
1836
0
    talloc_free(tmp_ctx);
1837
0
    return 0;
1838
0
  }
1839
1840
0
  DEBUG(3, (__location__ ": Will try to delete old keytab entries\n"));
1841
0
  while (!samba_krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
1842
0
    bool name_ok = false;
1843
0
    krb5_enctype kt_entry_enctype =
1844
0
      smb_krb5_kt_get_enctype_from_entry(&kt_entry);
1845
1846
0
    if (princ_s != NULL) {
1847
0
      ret = smb_krb5_unparse_name(tmp_ctx, context,
1848
0
                kt_entry.principal,
1849
0
                &ktprinc);
1850
0
      if (ret) {
1851
0
        DEBUG(1, (__location__
1852
0
            ": smb_krb5_unparse_name failed "
1853
0
            "(%s)\n", error_message(ret)));
1854
0
        goto out;
1855
0
      }
1856
1857
0
#ifdef HAVE_KRB5_KT_COMPARE
1858
0
      name_ok = krb5_kt_compare(context, &kt_entry,
1859
0
              princ, 0, 0);
1860
#else
1861
      name_ok = (strcmp(ktprinc, princ_s) == 0);
1862
#endif
1863
1864
0
      if (!name_ok) {
1865
0
        DEBUG(10, (__location__ ": ignoring keytab "
1866
0
             "entry principal %s, kvno = %d\n",
1867
0
             ktprinc, kt_entry.vno));
1868
1869
        /* Not a match,
1870
         * just free this entry and continue. */
1871
0
        ret = smb_krb5_kt_free_entry(context,
1872
0
                   &kt_entry);
1873
0
        ZERO_STRUCT(kt_entry);
1874
0
        if (ret) {
1875
0
          DEBUG(1, (__location__
1876
0
              ": smb_krb5_kt_free_entry "
1877
0
              "failed (%s)\n",
1878
0
              error_message(ret)));
1879
0
          goto out;
1880
0
        }
1881
1882
0
        TALLOC_FREE(ktprinc);
1883
0
        continue;
1884
0
      }
1885
1886
0
      TALLOC_FREE(ktprinc);
1887
0
    }
1888
1889
    /*------------------------------------------------------------
1890
     * Save the entries with kvno - 1. This is what microsoft does
1891
     * to allow people with existing sessions that have kvno - 1
1892
     * to still work. Otherwise, when the password for the machine
1893
     * changes, all kerberized sessions will 'break' until either
1894
     * the client reboots or the client's session key expires and
1895
     * they get a new session ticket with the new kvno.
1896
     * Some keytab files only store the kvno in 8bits, limit
1897
     * the compare accordingly.
1898
     */
1899
1900
0
    if (keep_old_kvno && ((kt_entry.vno & 0xff) == (old_kvno & 0xff))) {
1901
0
      DEBUG(5, (__location__ ": Saving previous (kvno %d) "
1902
0
          "entry for principal: %s.\n",
1903
0
          old_kvno,
1904
0
          princ_s != NULL ? princ_s : "UNKNOWN"));
1905
0
      continue;
1906
0
    }
1907
1908
0
    if (enctype_only &&
1909
0
        ((kt_entry.vno & 0xff) == (kvno & 0xff)) &&
1910
0
        (kt_entry_enctype != enctype))
1911
0
    {
1912
0
      DEBUG(5, (__location__ ": Saving entry with kvno [%d] "
1913
0
          "enctype [%d] for principal: %s.\n",
1914
0
          kvno, kt_entry_enctype,
1915
0
          princ_s != NULL ? princ_s : "UNKNOWN"));
1916
0
      continue;
1917
0
    }
1918
1919
0
    DEBUG(5, (__location__ ": Found old entry for principal: %s "
1920
0
        "(kvno %d) - trying to remove it.\n",
1921
0
        princ_s != NULL ? princ_s : "UNKNOWN",
1922
0
        kt_entry.vno));
1923
1924
0
    ret = krb5_kt_end_seq_get(context, keytab, &cursor);
1925
0
    ZERO_STRUCT(cursor);
1926
0
    if (ret) {
1927
0
      DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
1928
0
          "failed (%s)\n", error_message(ret)));
1929
0
      goto out;
1930
0
    }
1931
0
    ret = samba_krb5_kt_remove_entry(context, keytab, &kt_entry);
1932
0
    if (ret) {
1933
0
      DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
1934
0
          "failed (%s)\n", error_message(ret)));
1935
0
      goto out;
1936
0
    }
1937
1938
0
    DEBUG(5, (__location__ ": removed old entry for principal: "
1939
0
        "%s (kvno %d).\n",
1940
0
        princ_s != NULL ? princ_s : "UNKNOWN",
1941
0
        kt_entry.vno));
1942
1943
0
    ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1944
0
    if (ret) {
1945
0
      DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
1946
0
          "(%s)\n", error_message(ret)));
1947
0
      goto out;
1948
0
    }
1949
0
    ret = smb_krb5_kt_free_entry(context, &kt_entry);
1950
0
    ZERO_STRUCT(kt_entry);
1951
0
    if (ret) {
1952
0
      DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
1953
0
          "failed (%s)\n", error_message(ret)));
1954
0
      goto out;
1955
0
    }
1956
0
  }
1957
1958
0
out:
1959
0
  talloc_free(tmp_ctx);
1960
0
  if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
1961
0
    smb_krb5_kt_free_entry(context, &kt_entry);
1962
0
  }
1963
0
  if (!all_zero((uint8_t *)&cursor, sizeof(cursor))) {
1964
0
    krb5_kt_end_seq_get(context, keytab, &cursor);
1965
0
  }
1966
0
  return ret;
1967
0
}
1968
1969
#if defined(HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE) && \
1970
    defined(HAVE_KRB5_GET_CREDS_OPT_ALLOC) && \
1971
    defined(HAVE_KRB5_GET_CREDS)
1972
static krb5_error_code smb_krb5_get_credentials_for_user_opt(krb5_context context,
1973
                   krb5_ccache ccache,
1974
                   krb5_principal me,
1975
                   krb5_principal server,
1976
                   krb5_principal impersonate_princ,
1977
                   krb5_creds **out_creds)
1978
0
{
1979
0
  krb5_error_code ret;
1980
0
  krb5_get_creds_opt opt;
1981
1982
0
  ret = krb5_get_creds_opt_alloc(context, &opt);
1983
0
  if (ret) {
1984
0
    goto done;
1985
0
  }
1986
0
  krb5_get_creds_opt_add_options(context, opt, KRB5_GC_FORWARDABLE);
1987
1988
0
  if (impersonate_princ) {
1989
0
    ret = krb5_get_creds_opt_set_impersonate(context, opt,
1990
0
               impersonate_princ);
1991
0
    if (ret) {
1992
0
      goto done;
1993
0
    }
1994
0
  }
1995
1996
0
  ret = krb5_get_creds(context, opt, ccache, server, out_creds);
1997
0
  if (ret) {
1998
0
    goto done;
1999
0
  }
2000
2001
0
 done:
2002
0
  if (opt) {
2003
0
    krb5_get_creds_opt_free(context, opt);
2004
0
  }
2005
0
  return ret;
2006
0
}
2007
#endif /* HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE */
2008
2009
#ifdef HAVE_KRB5_GET_CREDENTIALS_FOR_USER
2010
2011
#if !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER
2012
krb5_error_code KRB5_CALLCONV
2013
krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
2014
                              krb5_ccache ccache, krb5_creds *in_creds,
2015
                              krb5_data *subject_cert,
2016
                              krb5_creds **out_creds);
2017
#endif /* !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER */
2018
2019
static krb5_error_code smb_krb5_get_credentials_for_user(krb5_context context,
2020
               krb5_ccache ccache,
2021
               krb5_principal me,
2022
               krb5_principal server,
2023
               krb5_principal impersonate_princ,
2024
               krb5_creds **out_creds)
2025
{
2026
  krb5_error_code ret;
2027
  krb5_creds in_creds;
2028
2029
  ZERO_STRUCT(in_creds);
2030
2031
  if (impersonate_princ) {
2032
2033
    in_creds.server = me;
2034
    in_creds.client = impersonate_princ;
2035
2036
    ret = krb5_get_credentials_for_user(context,
2037
                0, /* krb5_flags options */
2038
                ccache,
2039
                &in_creds,
2040
                NULL, /* krb5_data *subject_cert */
2041
                out_creds);
2042
  } else {
2043
    in_creds.client = me;
2044
    in_creds.server = server;
2045
2046
    ret = krb5_get_credentials(context, 0, ccache,
2047
             &in_creds, out_creds);
2048
  }
2049
2050
  return ret;
2051
}
2052
#endif /* HAVE_KRB5_GET_CREDENTIALS_FOR_USER */
2053
2054
/*
2055
 * smb_krb5_get_credentials
2056
 *
2057
 * @brief Get krb5 credentials for a server
2058
 *
2059
 * @param[in] context   An initialized krb5_context
2060
 * @param[in] ccache    An initialized krb5_ccache
2061
 * @param[in] me    The krb5_principal of the caller
2062
 * @param[in] server    The krb5_principal of the requested service
2063
 * @param[in] impersonate_princ The krb5_principal of a user to impersonate as (optional)
2064
 * @param[out] out_creds  The returned krb5_creds structure
2065
 * @return krb5_error_code
2066
 *
2067
 */
2068
krb5_error_code smb_krb5_get_credentials(krb5_context context,
2069
           krb5_ccache ccache,
2070
           krb5_principal me,
2071
           krb5_principal server,
2072
           krb5_principal impersonate_princ,
2073
           krb5_creds **out_creds)
2074
0
{
2075
0
  krb5_error_code ret;
2076
0
  krb5_creds *creds = NULL;
2077
2078
0
  if (out_creds != NULL) {
2079
0
    *out_creds = NULL;
2080
0
  }
2081
2082
0
  if (impersonate_princ) {
2083
0
#ifdef HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE /* Heimdal */
2084
0
    ret = smb_krb5_get_credentials_for_user_opt(context, ccache, me, server, impersonate_princ, &creds);
2085
#elif defined(HAVE_KRB5_GET_CREDENTIALS_FOR_USER) /* MIT */
2086
    ret = smb_krb5_get_credentials_for_user(context, ccache, me, server, impersonate_princ, &creds);
2087
#else
2088
    ret = ENOTSUP;
2089
#endif
2090
0
  } else {
2091
0
    krb5_creds in_creds;
2092
2093
0
    ZERO_STRUCT(in_creds);
2094
2095
0
    in_creds.client = me;
2096
0
    in_creds.server = server;
2097
2098
0
    ret = krb5_get_credentials(context, 0, ccache,
2099
0
             &in_creds, &creds);
2100
0
  }
2101
0
  if (ret) {
2102
0
    goto done;
2103
0
  }
2104
2105
0
  if (out_creds) {
2106
0
    *out_creds = creds;
2107
0
  }
2108
2109
0
 done:
2110
0
  if (creds && ret) {
2111
0
    krb5_free_creds(context, creds);
2112
0
  }
2113
2114
0
  return ret;
2115
0
}
2116
2117
/**
2118
 * @brief Initialize a krb5_keyblock with the given data.
2119
 *
2120
 * Initializes a new keyblock, allocates the contents for the key and
2121
 * copies the data into the keyblock.
2122
 *
2123
 * @param[in]  context  The library context
2124
 *
2125
 * @param[in]  enctype  The encryption type.
2126
 *
2127
 * @param[in]  data     The date to initialize the keyblock with.
2128
 *
2129
 * @param[in]  length   The length of the keyblock.
2130
 *
2131
 * @param[in]  key      Newly allocated keyblock structure.
2132
 *
2133
 * The key date must be freed using krb5_free_keyblock_contents() when it is
2134
 * no longer needed.
2135
 *
2136
 * @return 0 on success, a Kerberos error code otherwise.
2137
 */
2138
krb5_error_code smb_krb5_keyblock_init_contents(krb5_context context,
2139
            krb5_enctype enctype,
2140
            const void *data,
2141
            size_t length,
2142
            krb5_keyblock *key)
2143
0
{
2144
0
#if defined(HAVE_KRB5_KEYBLOCK_INIT)
2145
0
  return krb5_keyblock_init(context, enctype, data, length, key);
2146
#else
2147
  memset(key, 0, sizeof(krb5_keyblock));
2148
  KRB5_KEY_DATA(key) = SMB_MALLOC(length);
2149
  if (NULL == KRB5_KEY_DATA(key)) {
2150
    return ENOMEM;
2151
  }
2152
  memcpy(KRB5_KEY_DATA(key), data, length);
2153
  KRB5_KEY_LENGTH(key) = length;
2154
  KRB5_KEY_TYPE(key) = enctype;
2155
  return 0;
2156
#endif
2157
0
}
2158
2159
/**
2160
 * @brief Simulate a kinit by putting the tgt in the given credential cache.
2161
 *
2162
 * This function uses a keyblock rather than needing the original password.
2163
 *
2164
 * @param[in]  ctx      The library context
2165
 *
2166
 * @param[in]  cc       The credential cache to put the tgt in.
2167
 *
2168
 * @param[in]  principal The client principal
2169
 *
2170
 * @param[in]  keyblock  The keyblock to use.
2171
 *
2172
 * @param[in]  target_service The service name of the initial credentials (or NULL).
2173
 *
2174
 * @param[in]  krb_options Initial credential options.
2175
 *
2176
 * @param[in]  expire_time    A pointer to store the expiration time of the
2177
 *                            credentials (or NULL).
2178
 *
2179
 * @param[in]  kdc_time       A pointer to store the time when the ticket becomes
2180
 *                            valid (or NULL).
2181
 *
2182
 * @return 0 on success, a Kerberos error code otherwise.
2183
 */
2184
krb5_error_code smb_krb5_kinit_keyblock_ccache(krb5_context ctx,
2185
                 krb5_ccache cc,
2186
                 krb5_principal principal,
2187
                 krb5_keyblock *keyblock,
2188
                 const char *target_service,
2189
                 krb5_get_init_creds_opt *krb_options,
2190
                 time_t *expire_time,
2191
                 time_t *kdc_time)
2192
0
{
2193
0
  krb5_error_code code = 0;
2194
0
  krb5_creds my_creds;
2195
2196
0
#if defined(HAVE_KRB5_GET_INIT_CREDS_KEYBLOCK)
2197
0
  code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal,
2198
0
              keyblock, 0, target_service,
2199
0
              krb_options);
2200
#elif defined(HAVE_KRB5_GET_INIT_CREDS_KEYTAB)
2201
{
2202
#define SMB_CREDS_KEYTAB "MEMORY:tmp_kinit_keyblock_ccache"
2203
  char tmp_name[64] = {0};
2204
  krb5_keytab_entry entry;
2205
  krb5_keytab keytab;
2206
  int rc;
2207
2208
  memset(&entry, 0, sizeof(entry));
2209
  entry.principal = principal;
2210
  *(KRB5_KT_KEY(&entry)) = *keyblock;
2211
2212
  rc = snprintf(tmp_name, sizeof(tmp_name),
2213
          "%s-%p",
2214
          SMB_CREDS_KEYTAB,
2215
          &my_creds);
2216
  if (rc < 0) {
2217
    ZERO_STRUCT(entry);
2218
    return KRB5_KT_BADNAME;
2219
  }
2220
  code = krb5_kt_resolve(ctx, tmp_name, &keytab);
2221
  if (code) {
2222
    ZERO_STRUCT(entry);
2223
    return code;
2224
  }
2225
2226
  code = krb5_kt_add_entry(ctx, keytab, &entry);
2227
  ZERO_STRUCT(entry);
2228
  if (code) {
2229
    (void)krb5_kt_close(ctx, keytab);
2230
    goto done;
2231
  }
2232
2233
  code = krb5_get_init_creds_keytab(ctx, &my_creds, principal,
2234
            keytab, 0, target_service,
2235
            krb_options);
2236
  (void)krb5_kt_close(ctx, keytab);
2237
}
2238
#else
2239
#error krb5_get_init_creds_keyblock not available!
2240
#endif
2241
0
  if (code) {
2242
0
    return code;
2243
0
  }
2244
2245
#ifndef SAMBA4_USES_HEIMDAL /* MIT */
2246
  /*
2247
   * We need to store the principal as returned from the KDC to the
2248
   * credentials cache. If we don't do that the KRB5 library is not
2249
   * able to find the tickets it is looking for
2250
   */
2251
  principal = my_creds.client;
2252
#endif
2253
0
  code = krb5_cc_initialize(ctx, cc, principal);
2254
0
  if (code) {
2255
0
    goto done;
2256
0
  }
2257
2258
0
  code = krb5_cc_store_cred(ctx, cc, &my_creds);
2259
0
  if (code) {
2260
0
    goto done;
2261
0
  }
2262
2263
0
  if (expire_time) {
2264
0
    *expire_time = (time_t) my_creds.times.endtime;
2265
0
  }
2266
2267
0
  if (kdc_time) {
2268
0
    *kdc_time = (time_t) my_creds.times.starttime;
2269
0
  }
2270
2271
0
  code = 0;
2272
0
done:
2273
0
  krb5_free_cred_contents(ctx, &my_creds);
2274
0
  return code;
2275
0
}
2276
2277
/**
2278
 * @brief Simulate a kinit by putting the tgt in the given credential cache.
2279
 *
2280
 * @param[in]  ctx      The library context
2281
 *
2282
 * @param[in]  cc       The credential cache to put the tgt in.
2283
 *
2284
 * @param[in]  principal The client principal
2285
 *
2286
 * @param[in]  password  The password (or NULL).
2287
 *
2288
 * @param[in]  target_service The service name of the initial credentials (or NULL).
2289
 *
2290
 * @param[in]  krb_options Initial credential options.
2291
 *
2292
 * @param[in]  expire_time    A pointer to store the expiration time of the
2293
 *                            credentials (or NULL).
2294
 *
2295
 * @param[in]  kdc_time       A pointer to store the time when the ticket becomes
2296
 *                            valid (or NULL).
2297
 *
2298
 * @return 0 on success, a Kerberos error code otherwise.
2299
 */
2300
krb5_error_code smb_krb5_kinit_password_ccache(krb5_context ctx,
2301
                 krb5_ccache cc,
2302
                 krb5_principal principal,
2303
                 const char *password,
2304
                 const char *target_service,
2305
                 krb5_get_init_creds_opt *krb_options,
2306
                 time_t *expire_time,
2307
                 time_t *kdc_time)
2308
0
{
2309
0
  krb5_error_code code = 0;
2310
0
  krb5_creds my_creds;
2311
2312
0
  code = krb5_get_init_creds_password(ctx, &my_creds, principal,
2313
0
              password, NULL, NULL, 0,
2314
0
              target_service, krb_options);
2315
0
  if (code) {
2316
0
    return code;
2317
0
  }
2318
2319
  /*
2320
   * We need to store the principal as returned from the KDC to the
2321
   * credentials cache. If we don't do that the KRB5 library is not
2322
   * able to find the tickets it is looking for
2323
   */
2324
0
  principal = my_creds.client;
2325
0
  code = krb5_cc_initialize(ctx, cc, principal);
2326
0
  if (code) {
2327
0
    goto done;
2328
0
  }
2329
2330
0
  code = krb5_cc_store_cred(ctx, cc, &my_creds);
2331
0
  if (code) {
2332
0
    goto done;
2333
0
  }
2334
2335
0
  if (expire_time) {
2336
0
    *expire_time = (time_t) my_creds.times.endtime;
2337
0
  }
2338
2339
0
  if (kdc_time) {
2340
0
    *kdc_time = (time_t) my_creds.times.starttime;
2341
0
  }
2342
2343
0
  code = 0;
2344
0
done:
2345
0
  krb5_free_cred_contents(ctx, &my_creds);
2346
0
  return code;
2347
0
}
2348
2349
#ifdef SAMBA4_USES_HEIMDAL
2350
/**
2351
 * @brief Simulate a kinit by putting the tgt in the given credential cache.
2352
 *
2353
 * @param[in]  ctx      The library context
2354
 *
2355
 * @param[in]  cc       The credential cache to store the tgt in.
2356
 *
2357
 * @param[in]  principal The initial client principal.
2358
 *
2359
 * @param[in]  password  The password (or NULL).
2360
 *
2361
 * @param[in]  impersonate_principal The impersonation principal (or NULL).
2362
 *
2363
 * @param[in]  self_service The local service for S4U2Self if
2364
 *                          impersonate_principal is specified).
2365
 *
2366
 * @param[in]  target_service The service name of the initial credentials
2367
 *                            (kpasswd/REALM or a remote service). It defaults
2368
 *                            to the krbtgt if NULL.
2369
 *
2370
 * @param[in]  krb_options Initial credential options.
2371
 *
2372
 * @param[in]  expire_time    A pointer to store the expiration time of the
2373
 *                            credentials (or NULL).
2374
 *
2375
 * @param[in]  kdc_time       A pointer to store the time when the ticket becomes
2376
 *                            valid (or NULL).
2377
 *
2378
 * @return 0 on success, a Kerberos error code otherwise.
2379
 */
2380
krb5_error_code smb_krb5_kinit_s4u2_ccache(krb5_context ctx,
2381
             krb5_ccache store_cc,
2382
             krb5_principal init_principal,
2383
             const char *init_password,
2384
             krb5_principal impersonate_principal,
2385
             const char *self_service,
2386
             const char *target_service,
2387
             krb5_get_init_creds_opt *krb_options,
2388
             time_t *expire_time,
2389
             time_t *kdc_time)
2390
0
{
2391
0
  krb5_error_code code = 0;
2392
0
  krb5_get_creds_opt options;
2393
0
  krb5_principal store_principal;
2394
0
  krb5_creds store_creds;
2395
0
  krb5_creds *s4u2self_creds;
2396
0
  Ticket s4u2self_ticket;
2397
0
  size_t s4u2self_ticketlen;
2398
0
  krb5_creds *s4u2proxy_creds;
2399
0
  krb5_principal self_princ;
2400
0
  bool s4u2proxy;
2401
0
  krb5_principal target_princ;
2402
0
  krb5_ccache tmp_cc;
2403
0
  const char *self_realm;
2404
0
  const char *client_realm = NULL;
2405
0
  krb5_principal blacklist_principal = NULL;
2406
0
  krb5_principal whitelist_principal = NULL;
2407
2408
0
  code = krb5_get_init_creds_password(ctx, &store_creds,
2409
0
              init_principal,
2410
0
              init_password,
2411
0
              NULL, NULL,
2412
0
              0,
2413
0
              NULL,
2414
0
              krb_options);
2415
0
  if (code != 0) {
2416
0
    return code;
2417
0
  }
2418
2419
0
  store_principal = init_principal;
2420
2421
  /*
2422
   * We are trying S4U2Self now:
2423
   *
2424
   * As we do not want to expose our TGT in the
2425
   * krb5_ccache, which is also holds the impersonated creds.
2426
   *
2427
   * Some low level krb5/gssapi function might use the TGT
2428
   * identity and let the client act as our machine account.
2429
   *
2430
   * We need to avoid that and use a temporary krb5_ccache
2431
   * in order to pass our TGT to the krb5_get_creds() function.
2432
   */
2433
0
  code = smb_krb5_cc_new_unique_memory(ctx, NULL, NULL, &tmp_cc);
2434
0
  if (code != 0) {
2435
0
    krb5_free_cred_contents(ctx, &store_creds);
2436
0
    return code;
2437
0
  }
2438
2439
0
  code = krb5_cc_initialize(ctx, tmp_cc, store_creds.client);
2440
0
  if (code != 0) {
2441
0
    krb5_cc_destroy(ctx, tmp_cc);
2442
0
    krb5_free_cred_contents(ctx, &store_creds);
2443
0
    return code;
2444
0
  }
2445
2446
0
  code = krb5_cc_store_cred(ctx, tmp_cc, &store_creds);
2447
0
  if (code != 0) {
2448
0
    krb5_free_cred_contents(ctx, &store_creds);
2449
0
    krb5_cc_destroy(ctx, tmp_cc);
2450
0
    return code;
2451
0
  }
2452
2453
  /*
2454
   * we need to remember the client principal of our
2455
   * TGT and make sure the KDC does not return this
2456
   * in the impersonated tickets. This can happen
2457
   * if the KDC does not support S4U2Self and S4U2Proxy.
2458
   */
2459
0
  blacklist_principal = store_creds.client;
2460
0
  store_creds.client = NULL;
2461
0
  krb5_free_cred_contents(ctx, &store_creds);
2462
2463
  /*
2464
   * Check if we also need S4U2Proxy or if S4U2Self is
2465
   * enough in order to get a ticket for the target.
2466
   */
2467
0
  if (target_service == NULL) {
2468
0
    s4u2proxy = false;
2469
0
  } else if (strcmp(target_service, self_service) == 0) {
2470
0
    s4u2proxy = false;
2471
0
  } else {
2472
0
    s4u2proxy = true;
2473
0
  }
2474
2475
  /*
2476
   * For S4U2Self we need our own service principal,
2477
   * which belongs to our own realm (available on
2478
   * our client principal).
2479
   */
2480
0
  self_realm = krb5_principal_get_realm(ctx, init_principal);
2481
2482
0
  code = krb5_parse_name(ctx, self_service, &self_princ);
2483
0
  if (code != 0) {
2484
0
    krb5_free_principal(ctx, blacklist_principal);
2485
0
    krb5_cc_destroy(ctx, tmp_cc);
2486
0
    return code;
2487
0
  }
2488
2489
0
  code = krb5_principal_set_realm(ctx, self_princ, self_realm);
2490
0
  if (code != 0) {
2491
0
    krb5_free_principal(ctx, blacklist_principal);
2492
0
    krb5_free_principal(ctx, self_princ);
2493
0
    krb5_cc_destroy(ctx, tmp_cc);
2494
0
    return code;
2495
0
  }
2496
2497
0
  code = krb5_get_creds_opt_alloc(ctx, &options);
2498
0
  if (code != 0) {
2499
0
    krb5_free_principal(ctx, blacklist_principal);
2500
0
    krb5_free_principal(ctx, self_princ);
2501
0
    krb5_cc_destroy(ctx, tmp_cc);
2502
0
    return code;
2503
0
  }
2504
2505
0
  if (s4u2proxy) {
2506
    /*
2507
     * If we want S4U2Proxy, we need the forwardable flag
2508
     * on the S4U2Self ticket.
2509
     */
2510
0
    krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2511
0
  }
2512
2513
0
  code = krb5_get_creds_opt_set_impersonate(ctx, options,
2514
0
              impersonate_principal);
2515
0
  if (code != 0) {
2516
0
    krb5_get_creds_opt_free(ctx, options);
2517
0
    krb5_free_principal(ctx, blacklist_principal);
2518
0
    krb5_free_principal(ctx, self_princ);
2519
0
    krb5_cc_destroy(ctx, tmp_cc);
2520
0
    return code;
2521
0
  }
2522
2523
0
  code = krb5_get_creds(ctx, options, tmp_cc,
2524
0
            self_princ, &s4u2self_creds);
2525
0
  krb5_get_creds_opt_free(ctx, options);
2526
0
  krb5_free_principal(ctx, self_princ);
2527
0
  if (code != 0) {
2528
0
    krb5_free_principal(ctx, blacklist_principal);
2529
0
    krb5_cc_destroy(ctx, tmp_cc);
2530
0
    return code;
2531
0
  }
2532
2533
0
  if (!s4u2proxy) {
2534
0
    krb5_cc_destroy(ctx, tmp_cc);
2535
2536
    /*
2537
     * Now make sure we store the impersonated principal
2538
     * and creds instead of the TGT related stuff
2539
     * in the krb5_ccache of the caller.
2540
     */
2541
0
    code = krb5_copy_creds_contents(ctx, s4u2self_creds,
2542
0
            &store_creds);
2543
0
    krb5_free_creds(ctx, s4u2self_creds);
2544
0
    if (code != 0) {
2545
0
      return code;
2546
0
    }
2547
2548
    /*
2549
     * It's important to store the principal the KDC
2550
     * returned, as otherwise the caller would not find
2551
     * the S4U2Self ticket in the krb5_ccache lookup.
2552
     */
2553
0
    store_principal = store_creds.client;
2554
0
    goto store;
2555
0
  }
2556
2557
  /*
2558
   * We are trying S4U2Proxy:
2559
   *
2560
   * We need the ticket from the S4U2Self step
2561
   * and our TGT in order to get the delegated ticket.
2562
   */
2563
0
  code = decode_Ticket((const uint8_t *)s4u2self_creds->ticket.data,
2564
0
           s4u2self_creds->ticket.length,
2565
0
           &s4u2self_ticket,
2566
0
           &s4u2self_ticketlen);
2567
0
  if (code != 0) {
2568
0
    krb5_free_creds(ctx, s4u2self_creds);
2569
0
    krb5_free_principal(ctx, blacklist_principal);
2570
0
    krb5_cc_destroy(ctx, tmp_cc);
2571
0
    return code;
2572
0
  }
2573
2574
  /*
2575
   * we need to remember the client principal of the
2576
   * S4U2Self stage and as it needs to match the one we
2577
   * will get for the S4U2Proxy stage. We need this
2578
   * in order to detect KDCs which does not support S4U2Proxy.
2579
   */
2580
0
  whitelist_principal = s4u2self_creds->client;
2581
0
  s4u2self_creds->client = NULL;
2582
0
  krb5_free_creds(ctx, s4u2self_creds);
2583
2584
  /*
2585
   * For S4U2Proxy we also got a target service principal,
2586
   * which also belongs to our own realm (available on
2587
   * our client principal).
2588
   */
2589
0
  code = krb5_parse_name(ctx, target_service, &target_princ);
2590
0
  if (code != 0) {
2591
0
    free_Ticket(&s4u2self_ticket);
2592
0
    krb5_free_principal(ctx, whitelist_principal);
2593
0
    krb5_free_principal(ctx, blacklist_principal);
2594
0
    krb5_cc_destroy(ctx, tmp_cc);
2595
0
    return code;
2596
0
  }
2597
2598
0
  code = krb5_principal_set_realm(ctx, target_princ, self_realm);
2599
0
  if (code != 0) {
2600
0
    free_Ticket(&s4u2self_ticket);
2601
0
    krb5_free_principal(ctx, target_princ);
2602
0
    krb5_free_principal(ctx, whitelist_principal);
2603
0
    krb5_free_principal(ctx, blacklist_principal);
2604
0
    krb5_cc_destroy(ctx, tmp_cc);
2605
0
    return code;
2606
0
  }
2607
2608
0
  code = krb5_get_creds_opt_alloc(ctx, &options);
2609
0
  if (code != 0) {
2610
0
    free_Ticket(&s4u2self_ticket);
2611
0
    krb5_free_principal(ctx, target_princ);
2612
0
    krb5_free_principal(ctx, whitelist_principal);
2613
0
    krb5_free_principal(ctx, blacklist_principal);
2614
0
    krb5_cc_destroy(ctx, tmp_cc);
2615
0
    return code;
2616
0
  }
2617
2618
0
  krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2619
0
  krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_CONSTRAINED_DELEGATION);
2620
2621
0
  code = krb5_get_creds_opt_set_ticket(ctx, options, &s4u2self_ticket);
2622
0
  free_Ticket(&s4u2self_ticket);
2623
0
  if (code != 0) {
2624
0
    krb5_get_creds_opt_free(ctx, options);
2625
0
    krb5_free_principal(ctx, target_princ);
2626
0
    krb5_free_principal(ctx, whitelist_principal);
2627
0
    krb5_free_principal(ctx, blacklist_principal);
2628
0
    krb5_cc_destroy(ctx, tmp_cc);
2629
0
    return code;
2630
0
  }
2631
2632
0
  code = krb5_get_creds(ctx, options, tmp_cc,
2633
0
            target_princ, &s4u2proxy_creds);
2634
0
  krb5_get_creds_opt_free(ctx, options);
2635
0
  krb5_free_principal(ctx, target_princ);
2636
0
  krb5_cc_destroy(ctx, tmp_cc);
2637
0
  if (code != 0) {
2638
0
    krb5_free_principal(ctx, whitelist_principal);
2639
0
    krb5_free_principal(ctx, blacklist_principal);
2640
0
    return code;
2641
0
  }
2642
2643
  /*
2644
   * Now make sure we store the impersonated principal
2645
   * and creds instead of the TGT related stuff
2646
   * in the krb5_ccache of the caller.
2647
   */
2648
0
  code = krb5_copy_creds_contents(ctx, s4u2proxy_creds,
2649
0
          &store_creds);
2650
0
  krb5_free_creds(ctx, s4u2proxy_creds);
2651
0
  if (code != 0) {
2652
0
    krb5_free_principal(ctx, whitelist_principal);
2653
0
    krb5_free_principal(ctx, blacklist_principal);
2654
0
    return code;
2655
0
  }
2656
2657
  /*
2658
   * It's important to store the principal the KDC
2659
   * returned, as otherwise the caller would not find
2660
   * the S4U2Self ticket in the krb5_ccache lookup.
2661
   */
2662
0
  store_principal = store_creds.client;
2663
2664
0
 store:
2665
0
  if (blacklist_principal &&
2666
0
      krb5_principal_compare(ctx, store_creds.client, blacklist_principal)) {
2667
0
    char *sp = NULL;
2668
0
    char *ip = NULL;
2669
2670
0
    code = krb5_unparse_name(ctx, blacklist_principal, &sp);
2671
0
    if (code != 0) {
2672
0
      sp = NULL;
2673
0
    }
2674
0
    code = krb5_unparse_name(ctx, impersonate_principal, &ip);
2675
0
    if (code != 0) {
2676
0
      ip = NULL;
2677
0
    }
2678
0
    DBG_WARNING("KDC returned self principal[%s] while impersonating [%s]\n",
2679
0
          sp?sp:"<no memory>",
2680
0
          ip?ip:"<no memory>");
2681
2682
0
    SAFE_FREE(sp);
2683
0
    SAFE_FREE(ip);
2684
2685
0
    krb5_free_principal(ctx, whitelist_principal);
2686
0
    krb5_free_principal(ctx, blacklist_principal);
2687
0
    krb5_free_cred_contents(ctx, &store_creds);
2688
0
    return KRB5_FWD_BAD_PRINCIPAL;
2689
0
  }
2690
0
  if (blacklist_principal) {
2691
0
    krb5_free_principal(ctx, blacklist_principal);
2692
0
  }
2693
2694
0
  if (whitelist_principal &&
2695
0
      !krb5_principal_compare(ctx, store_creds.client, whitelist_principal)) {
2696
0
    char *sp = NULL;
2697
0
    char *ep = NULL;
2698
2699
0
    code = krb5_unparse_name(ctx, store_creds.client, &sp);
2700
0
    if (code != 0) {
2701
0
      sp = NULL;
2702
0
    }
2703
0
    code = krb5_unparse_name(ctx, whitelist_principal, &ep);
2704
0
    if (code != 0) {
2705
0
      ep = NULL;
2706
0
    }
2707
0
    DBG_WARNING("KDC returned wrong principal[%s] we expected [%s]\n",
2708
0
          sp?sp:"<no memory>",
2709
0
          ep?ep:"<no memory>");
2710
2711
0
    SAFE_FREE(sp);
2712
0
    SAFE_FREE(ep);
2713
2714
0
    krb5_free_principal(ctx, whitelist_principal);
2715
0
    krb5_free_cred_contents(ctx, &store_creds);
2716
0
    return KRB5_FWD_BAD_PRINCIPAL;
2717
0
  }
2718
0
  if (whitelist_principal) {
2719
0
    krb5_free_principal(ctx, whitelist_principal);
2720
0
  }
2721
2722
0
  code = krb5_cc_initialize(ctx, store_cc, store_principal);
2723
0
  if (code != 0) {
2724
0
    krb5_free_cred_contents(ctx, &store_creds);
2725
0
    return code;
2726
0
  }
2727
2728
0
  code = krb5_cc_store_cred(ctx, store_cc, &store_creds);
2729
0
  if (code != 0) {
2730
0
    krb5_free_cred_contents(ctx, &store_creds);
2731
0
    return code;
2732
0
  }
2733
2734
0
  client_realm = krb5_principal_get_realm(ctx, store_creds.client);
2735
0
  if (client_realm != NULL) {
2736
    /*
2737
     * Because the CANON flag doesn't have any impact
2738
     * on the impersonate_principal => store_creds.client
2739
     * realm mapping. We need to store the credentials twice,
2740
     * once with the returned realm and once with the
2741
     * realm of impersonate_principal.
2742
     */
2743
0
    code = krb5_principal_set_realm(ctx, store_creds.server,
2744
0
            client_realm);
2745
0
    if (code != 0) {
2746
0
      krb5_free_cred_contents(ctx, &store_creds);
2747
0
      return code;
2748
0
    }
2749
2750
0
    code = krb5_cc_store_cred(ctx, store_cc, &store_creds);
2751
0
    if (code != 0) {
2752
0
      krb5_free_cred_contents(ctx, &store_creds);
2753
0
      return code;
2754
0
    }
2755
0
  }
2756
2757
0
  if (expire_time) {
2758
0
    *expire_time = (time_t) store_creds.times.endtime;
2759
0
  }
2760
2761
0
  if (kdc_time) {
2762
0
    *kdc_time = (time_t) store_creds.times.starttime;
2763
0
  }
2764
2765
0
  krb5_free_cred_contents(ctx, &store_creds);
2766
2767
0
  return 0;
2768
0
}
2769
2770
#else /* MIT */
2771
2772
static bool princ_compare_no_dollar(krb5_context ctx,
2773
            krb5_principal a,
2774
            krb5_principal b)
2775
{
2776
  krb5_principal mod = NULL;
2777
  bool cmp;
2778
2779
  if (a->length == 1 && b->length == 1 &&
2780
      a->data[0].length != 0 && b->data[0].length != 0 &&
2781
      a->data[0].data[a->data[0].length - 1] !=
2782
      b->data[0].data[b->data[0].length - 1]) {
2783
    if (a->data[0].data[a->data[0].length - 1] == '$') {
2784
      mod = a;
2785
      mod->data[0].length--;
2786
    } else if (b->data[0].data[b->data[0].length - 1] == '$') {
2787
      mod = b;
2788
      mod->data[0].length--;
2789
    }
2790
  }
2791
2792
  cmp = krb5_principal_compare_flags(ctx,
2793
             a,
2794
             b,
2795
             KRB5_PRINCIPAL_COMPARE_CASEFOLD);
2796
  if (mod != NULL) {
2797
    mod->data[0].length++;
2798
  }
2799
2800
  return cmp;
2801
}
2802
2803
krb5_error_code smb_krb5_kinit_s4u2_ccache(krb5_context ctx,
2804
             krb5_ccache store_cc,
2805
             krb5_principal init_principal,
2806
             const char *init_password,
2807
             krb5_principal impersonate_principal,
2808
             const char *self_service,
2809
             const char *target_service,
2810
             krb5_get_init_creds_opt *krb_options,
2811
             time_t *expire_time,
2812
             time_t *kdc_time)
2813
{
2814
  krb5_error_code code;
2815
  krb5_principal self_princ = NULL;
2816
  krb5_principal target_princ = NULL;
2817
  krb5_creds *store_creds = NULL;
2818
  krb5_creds *s4u2self_creds = NULL;
2819
  krb5_creds *s4u2proxy_creds = NULL;
2820
  krb5_creds init_creds = {0};
2821
  krb5_creds mcreds = {0};
2822
  krb5_flags options = KRB5_GC_NO_STORE;
2823
  krb5_ccache tmp_cc;
2824
  bool s4u2proxy = false;
2825
  bool ok;
2826
2827
  code = smb_krb5_cc_new_unique_memory(ctx, NULL, NULL, &tmp_cc);
2828
  if (code != 0) {
2829
    return code;
2830
  }
2831
2832
  code = krb5_get_init_creds_password(ctx,
2833
              &init_creds,
2834
              init_principal,
2835
              init_password,
2836
              NULL,
2837
              NULL,
2838
              0,
2839
              NULL,
2840
              krb_options);
2841
  if (code != 0) {
2842
    goto done;
2843
  }
2844
2845
  code = krb5_cc_initialize(ctx, tmp_cc, init_creds.client);
2846
  if (code != 0) {
2847
    goto done;
2848
  }
2849
2850
  code = krb5_cc_store_cred(ctx, tmp_cc, &init_creds);
2851
  if (code != 0) {
2852
    goto done;
2853
  }
2854
2855
  /*
2856
   * Check if we also need S4U2Proxy or if S4U2Self is
2857
   * enough in order to get a ticket for the target.
2858
   */
2859
  if (target_service == NULL) {
2860
    s4u2proxy = false;
2861
  } else if (strcmp(target_service, self_service) == 0) {
2862
    s4u2proxy = false;
2863
  } else {
2864
    s4u2proxy = true;
2865
  }
2866
2867
  code = krb5_parse_name(ctx, self_service, &self_princ);
2868
  if (code != 0) {
2869
    goto done;
2870
  }
2871
2872
  /*
2873
   * MIT lacks aliases support in S4U, for S4U2Self we require the tgt
2874
   * client and the request server to be the same principal name.
2875
   */
2876
  ok = princ_compare_no_dollar(ctx, init_creds.client, self_princ);
2877
  if (!ok) {
2878
    code = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2879
    goto done;
2880
  }
2881
2882
  mcreds.client = impersonate_principal;
2883
  mcreds.server = init_creds.client;
2884
2885
  code = krb5_get_credentials_for_user(ctx, options, tmp_cc, &mcreds,
2886
               NULL, &s4u2self_creds);
2887
  if (code != 0) {
2888
    goto done;
2889
  }
2890
2891
  if (s4u2proxy) {
2892
    code = krb5_parse_name(ctx, target_service, &target_princ);
2893
    if (code != 0) {
2894
      goto done;
2895
    }
2896
2897
    mcreds.client = init_creds.client;
2898
    mcreds.server = target_princ;
2899
    mcreds.second_ticket = s4u2self_creds->ticket;
2900
2901
    code = krb5_get_credentials(ctx, options |
2902
              KRB5_GC_CONSTRAINED_DELEGATION,
2903
              tmp_cc, &mcreds, &s4u2proxy_creds);
2904
    if (code != 0) {
2905
      goto done;
2906
    }
2907
2908
    /* Check KDC support of S4U2Proxy extension */
2909
    if (!krb5_principal_compare(ctx, s4u2self_creds->client,
2910
              s4u2proxy_creds->client)) {
2911
      code = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2912
      goto done;
2913
    }
2914
2915
    store_creds = s4u2proxy_creds;
2916
  } else {
2917
    store_creds = s4u2self_creds;;
2918
2919
    /* We need to save the ticket with the requested server name
2920
     * or the caller won't be able to find it in cache. */
2921
    if (!krb5_principal_compare(ctx, self_princ,
2922
      store_creds->server)) {
2923
      krb5_free_principal(ctx, store_creds->server);
2924
      store_creds->server = NULL;
2925
      code = krb5_copy_principal(ctx, self_princ,
2926
               &store_creds->server);
2927
      if (code != 0) {
2928
        goto done;
2929
      }
2930
    }
2931
  }
2932
2933
  code = krb5_cc_initialize(ctx, store_cc, store_creds->client);
2934
  if (code != 0) {
2935
    goto done;
2936
  }
2937
2938
  code = krb5_cc_store_cred(ctx, store_cc, store_creds);
2939
  if (code != 0) {
2940
    goto done;
2941
  }
2942
2943
  if (expire_time) {
2944
    *expire_time = (time_t) store_creds->times.endtime;
2945
  }
2946
2947
  if (kdc_time) {
2948
    *kdc_time = (time_t) store_creds->times.starttime;
2949
  }
2950
2951
done:
2952
  krb5_cc_destroy(ctx, tmp_cc);
2953
  krb5_free_cred_contents(ctx, &init_creds);
2954
  krb5_free_creds(ctx, s4u2self_creds);
2955
  krb5_free_creds(ctx, s4u2proxy_creds);
2956
  krb5_free_principal(ctx, self_princ);
2957
  krb5_free_principal(ctx, target_princ);
2958
2959
  return code;
2960
}
2961
#endif
2962
2963
#if !defined(HAVE_KRB5_MAKE_PRINCIPAL) && defined(HAVE_KRB5_BUILD_PRINCIPAL_ALLOC_VA)
2964
/**
2965
 * @brief Create a principal name using a variable argument list.
2966
 *
2967
 * @param[in]  context  The library context.
2968
 *
2969
 * @param[inout]  principal A pointer to the principal structure.
2970
 *
2971
 * @param[in]  _realm    The realm to use. If NULL then the function will
2972
 *                       get the default realm name.
2973
 *
2974
 * @param[in]  ...       A list of 'char *' components, ending with NULL.
2975
 *
2976
 * Use krb5_free_principal() to free the principal when it is no longer needed.
2977
 *
2978
 * @return 0 on success, a Kerberos error code otherwise.
2979
 */
2980
krb5_error_code smb_krb5_make_principal(krb5_context context,
2981
          krb5_principal *principal,
2982
          const char *_realm, ...)
2983
{
2984
  krb5_error_code code;
2985
  bool free_realm;
2986
  char *realm;
2987
  va_list ap;
2988
2989
  if (_realm) {
2990
    realm = discard_const_p(char, _realm);
2991
    free_realm = false;
2992
  } else {
2993
    code = krb5_get_default_realm(context, &realm);
2994
    if (code) {
2995
      return code;
2996
    }
2997
    free_realm = true;
2998
  }
2999
3000
  va_start(ap, _realm);
3001
  code = krb5_build_principal_alloc_va(context, principal,
3002
               strlen(realm), realm,
3003
               ap);
3004
  va_end(ap);
3005
3006
  if (free_realm) {
3007
    krb5_free_default_realm(context, realm);
3008
  }
3009
3010
  return code;
3011
}
3012
#endif
3013
3014
#if !defined(HAVE_KRB5_CC_GET_LIFETIME) && defined(HAVE_KRB5_CC_RETRIEVE_CRED)
3015
/**
3016
 * @brief Get the lifetime of the initial ticket in the cache.
3017
 *
3018
 * @param[in]  context  The kerberos context.
3019
 *
3020
 * @param[in]  id       The credential cache to get the ticket lifetime.
3021
 *
3022
 * @param[out] t        A pointer to a time value to store the lifetime.
3023
 *
3024
 * @return              0 on success, a krb5_error_code on error.
3025
 */
3026
krb5_error_code smb_krb5_cc_get_lifetime(krb5_context context,
3027
           krb5_ccache id,
3028
           time_t *t)
3029
{
3030
  krb5_cc_cursor cursor;
3031
  krb5_error_code kerr;
3032
  krb5_creds cred;
3033
  krb5_timestamp endtime = 0;
3034
  krb5_timestamp now;
3035
  char *realm = NULL;
3036
  TALLOC_CTX *mem_ctx = NULL;
3037
3038
  *t = 0;
3039
3040
  kerr = krb5_timeofday(context, &now);
3041
  if (kerr) {
3042
    return kerr;
3043
  }
3044
3045
  kerr = krb5_cc_start_seq_get(context, id, &cursor);
3046
  if (kerr) {
3047
    return kerr;
3048
  }
3049
3050
  mem_ctx = talloc_stackframe();
3051
  if (mem_ctx == NULL) {
3052
          krb5_cc_end_seq_get(context, id, &cursor);
3053
    return ENOMEM;
3054
  }
3055
3056
  while ((kerr = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
3057
    if (krb5_is_config_principal(context, cred.server)) {
3058
      krb5_free_cred_contents(context, &cred);
3059
      continue;
3060
    }
3061
3062
    realm = smb_krb5_principal_get_realm(mem_ctx, context, cred.server);
3063
    if (realm == NULL) {
3064
      krb5_free_cred_contents(context, &cred);
3065
      kerr = ENOMEM;
3066
      break;
3067
    }
3068
3069
    /*
3070
     * 'X-GSSPROXY:' is the realm for an encrypted credential stored
3071
     * by the GSSProxy. There are no other creds in such ccache and
3072
     * we cannot see the actual lifetime (it is set to 0),
3073
     * indicate to the caller they need to handle this themselves.
3074
     */
3075
    if (strcmp(realm, "X-GSSPROXY:") == 0) {
3076
      krb5_free_cred_contents(context, &cred);
3077
      kerr = KRB5_PLUGIN_NO_HANDLE;
3078
      break;
3079
    }
3080
3081
#ifndef HAVE_FLAGS_IN_KRB5_CREDS
3082
    if (cred.ticket_flags & TKT_FLG_INITIAL) {
3083
#else
3084
    if (cred.flags.b.initial) {
3085
#endif
3086
      if (now < cred.times.endtime) {
3087
        endtime = cred.times.endtime;
3088
      }
3089
      krb5_free_cred_contents(context, &cred);
3090
      break;
3091
    }
3092
3093
    if (cred.times.endtime <= now) {
3094
      /* already expired */
3095
      krb5_free_cred_contents(context, &cred);
3096
      continue;
3097
    }
3098
3099
    /**
3100
     * If there was no krbtgt, use the shortest lifetime of
3101
     * service tickets that have yet to expire.  If all
3102
     * credentials are expired, krb5_cc_get_lifetime() will fail.
3103
     */
3104
    if (endtime == 0 || cred.times.endtime < endtime) {
3105
      endtime = cred.times.endtime;
3106
    }
3107
    krb5_free_cred_contents(context, &cred);
3108
  }
3109
3110
  krb5_cc_end_seq_get(context, id, &cursor);
3111
  talloc_free(mem_ctx);
3112
  if (kerr == ENOMEM || kerr == KRB5_PLUGIN_NO_HANDLE) {
3113
    return kerr;
3114
  }
3115
3116
  if (now < endtime) {
3117
    *t = (time_t) (endtime - now);
3118
    kerr = 0;
3119
  }
3120
3121
  return kerr;
3122
}
3123
#endif /* HAVE_KRB5_CC_GET_LIFETIME */
3124
3125
#if !defined(HAVE_KRB5_FREE_CHECKSUM_CONTENTS) && defined(HAVE_FREE_CHECKSUM)
3126
void smb_krb5_free_checksum_contents(krb5_context ctx, krb5_checksum *cksum)
3127
0
{
3128
0
  free_Checksum(cksum);
3129
0
}
3130
#endif
3131
3132
/**
3133
 * @brief Compute a checksum operating on a keyblock.
3134
 *
3135
 * This function computes a checksum over a PAC using the keyblock for a keyed
3136
 * checksum.
3137
 *
3138
 * @param[in]  mem_ctx A talloc context to allocate the signature on.
3139
 *
3140
 * @param[in]  pac_data The PAC as input.
3141
 *
3142
 * @param[in]  context  The library context.
3143
 *
3144
 * @param[in]  keyblock Encryption key for a keyed checksum.
3145
 *
3146
 * @param[out] sig_type The checksum type
3147
 *
3148
 * @param[out] sig_blob The talloc'ed checksum
3149
 *
3150
 * The caller must free the sig_blob with talloc_free() when it is not needed
3151
 * anymore.
3152
 *
3153
 * @return 0 on success, a Kerberos error code otherwise.
3154
 */
3155
krb5_error_code smb_krb5_make_pac_checksum(TALLOC_CTX *mem_ctx,
3156
             DATA_BLOB *pac_data,
3157
             krb5_context context,
3158
             const krb5_keyblock *keyblock,
3159
             uint32_t *sig_type,
3160
             DATA_BLOB *sig_blob)
3161
0
{
3162
0
  krb5_error_code ret;
3163
0
  krb5_checksum cksum;
3164
0
#if defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CREATE_CHECKSUM)
3165
0
  krb5_crypto crypto;
3166
3167
3168
0
  ret = krb5_crypto_init(context,
3169
0
             keyblock,
3170
0
             0,
3171
0
             &crypto);
3172
0
  if (ret) {
3173
0
    DEBUG(0,("krb5_crypto_init() failed: %s\n",
3174
0
        smb_get_krb5_error_message(context, ret, mem_ctx)));
3175
0
    return ret;
3176
0
  }
3177
0
  ret = krb5_create_checksum(context,
3178
0
           crypto,
3179
0
           KRB5_KU_OTHER_CKSUM,
3180
0
           0,
3181
0
           pac_data->data,
3182
0
           pac_data->length,
3183
0
           &cksum);
3184
0
  if (ret) {
3185
0
    DEBUG(2, ("PAC Verification failed: %s\n",
3186
0
        smb_get_krb5_error_message(context, ret, mem_ctx)));
3187
0
  }
3188
3189
0
  krb5_crypto_destroy(context, crypto);
3190
3191
0
  if (ret) {
3192
0
    return ret;
3193
0
  }
3194
3195
0
  *sig_type = cksum.cksumtype;
3196
0
  *sig_blob = data_blob_talloc(mem_ctx,
3197
0
          cksum.checksum.data,
3198
0
          cksum.checksum.length);
3199
#elif defined(HAVE_KRB5_C_MAKE_CHECKSUM)
3200
  krb5_data input;
3201
3202
  input.data = (char *)pac_data->data;
3203
  input.length = pac_data->length;
3204
3205
  ret = krb5_c_make_checksum(context,
3206
           0,
3207
           keyblock,
3208
           KRB5_KEYUSAGE_APP_DATA_CKSUM,
3209
           &input,
3210
           &cksum);
3211
  if (ret) {
3212
    DEBUG(2, ("PAC Verification failed: %s\n",
3213
        smb_get_krb5_error_message(context, ret, mem_ctx)));
3214
    return ret;
3215
  }
3216
3217
  *sig_type = cksum.checksum_type;
3218
  *sig_blob = data_blob_talloc(mem_ctx,
3219
          cksum.contents,
3220
          cksum.length);
3221
3222
#else
3223
#error krb5_create_checksum or krb5_c_make_checksum not available
3224
#endif /* HAVE_KRB5_C_MAKE_CHECKSUM */
3225
0
  smb_krb5_free_checksum_contents(context, &cksum);
3226
3227
0
  return 0;
3228
0
}
3229
3230
3231
/**
3232
 * @brief Get realm of a principal
3233
 *
3234
 * @param[in] mem_ctx   The talloc ctx to put the result on
3235
 *
3236
 * @param[in] context   The library context
3237
 *
3238
 * @param[in] principal The principal to get the realm from.
3239
 *
3240
 * @return A talloced string with the realm or NULL if an error occurred.
3241
 */
3242
char *smb_krb5_principal_get_realm(TALLOC_CTX *mem_ctx,
3243
           krb5_context context,
3244
           krb5_const_principal principal)
3245
0
{
3246
0
#ifdef HAVE_KRB5_PRINCIPAL_GET_REALM /* Heimdal */
3247
0
  const char *realm = NULL;
3248
3249
0
  realm = krb5_principal_get_realm(context, principal);
3250
0
  if (realm == NULL) {
3251
0
    return NULL;
3252
0
  }
3253
3254
0
  return talloc_strdup(mem_ctx, realm);
3255
#elif defined(krb5_princ_realm) /* MIT */
3256
  const krb5_data *realm = NULL;
3257
3258
  realm = krb5_princ_realm(context, principal);
3259
  if (realm == NULL) {
3260
    return NULL;
3261
  }
3262
3263
  return talloc_strndup(mem_ctx, realm->data, realm->length);
3264
#else
3265
#error UNKNOWN_GET_PRINC_REALM_FUNCTIONS
3266
#endif
3267
0
}
3268
3269
/**
3270
 * @brief Get realm of a principal
3271
 *
3272
 * @param[in] context   The library context
3273
 *
3274
 * @param[in] principal The principal to set the realm
3275
 *
3276
 * @param[in] realm     The realm as a string to set.
3277
 *
3278
 * @return 0 on success, a Kerberos error code otherwise.
3279
 */
3280
krb5_error_code smb_krb5_principal_set_realm(krb5_context context,
3281
               krb5_principal principal,
3282
               const char *realm)
3283
0
{
3284
0
#ifdef HAVE_KRB5_PRINCIPAL_SET_REALM /* Heimdal */
3285
0
  return krb5_principal_set_realm(context, principal, realm);
3286
#elif defined(krb5_princ_realm) && defined(krb5_princ_set_realm) /* MIT */
3287
  krb5_error_code ret;
3288
  krb5_data data;
3289
  krb5_data *old_data;
3290
3291
  old_data = krb5_princ_realm(context, principal);
3292
3293
  ret = smb_krb5_copy_data_contents(&data,
3294
            realm,
3295
            strlen(realm));
3296
  if (ret) {
3297
    return ret;
3298
  }
3299
3300
  /* free realm before setting */
3301
  free(old_data->data);
3302
3303
  krb5_princ_set_realm(context, principal, &data);
3304
3305
  return ret;
3306
#else
3307
#error UNKNOWN_PRINC_SET_REALM_FUNCTION
3308
#endif
3309
0
}
3310
3311
3312
/**
3313
 * @brief Get the realm from the service hostname.
3314
 *
3315
 * This function will look for a domain realm mapping in the [domain_realm]
3316
 * section of the krb5.conf first and fallback to extract the realm from
3317
 * the provided service hostname. As a last resort it will return the
3318
 * provided client_realm.
3319
 *
3320
 * @param[in]  mem_ctx     The talloc context
3321
 *
3322
 * @param[in]  hostname    The service hostname
3323
 *
3324
 * @param[in]  client_realm  If we can not find a mapping, fall back to
3325
 *                           this realm.
3326
 *
3327
 * @return The realm to use for the service hostname, NULL if a fatal error
3328
 *         occurred.
3329
 */
3330
char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx,
3331
               const char *hostname,
3332
               const char *client_realm)
3333
0
{
3334
0
#if defined(HAVE_KRB5_REALM_TYPE)
3335
  /* Heimdal. */
3336
0
  krb5_realm *realm_list = NULL;
3337
#else
3338
  /* MIT */
3339
  char **realm_list = NULL;
3340
#endif
3341
0
  char *realm = NULL;
3342
0
  krb5_error_code kerr;
3343
0
  krb5_context ctx = NULL;
3344
3345
0
  kerr = smb_krb5_init_context_common(&ctx);
3346
0
  if (kerr) {
3347
0
    DBG_ERR("kerberos init context failed (%s)\n",
3348
0
      error_message(kerr));
3349
0
    return NULL;
3350
0
  }
3351
3352
0
  kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
3353
0
  if (kerr == KRB5_ERR_HOST_REALM_UNKNOWN) {
3354
0
    realm_list = NULL;
3355
0
    kerr = 0;
3356
0
  }
3357
0
  if (kerr != 0) {
3358
0
    DEBUG(3,("kerberos_get_realm_from_hostname %s: "
3359
0
      "failed %s\n",
3360
0
      hostname ? hostname : "(NULL)",
3361
0
      error_message(kerr) ));
3362
0
    goto out;
3363
0
  }
3364
3365
0
  if (realm_list != NULL &&
3366
0
      realm_list[0] != NULL &&
3367
0
      realm_list[0][0] != '\0') {
3368
0
    realm = talloc_strdup(mem_ctx, realm_list[0]);
3369
0
    if (realm == NULL) {
3370
0
      goto out;
3371
0
    }
3372
0
  } else {
3373
0
    const char *p = NULL;
3374
3375
    /*
3376
     * "dc6.samba2003.example.com"
3377
     * returns a realm of "SAMBA2003.EXAMPLE.COM"
3378
     *
3379
     * "dc6." returns realm as NULL
3380
     */
3381
0
    p = strchr_m(hostname, '.');
3382
0
    if (p != NULL && p[1] != '\0') {
3383
0
      realm = talloc_strdup_upper(mem_ctx, p + 1);
3384
0
      if (realm == NULL) {
3385
0
        goto out;
3386
0
      }
3387
0
    }
3388
0
  }
3389
3390
0
  if (realm == NULL) {
3391
0
    realm = talloc_strdup(mem_ctx, client_realm);
3392
0
  }
3393
3394
0
  out:
3395
3396
0
  if (ctx) {
3397
0
    if (realm_list) {
3398
0
      krb5_free_host_realm(ctx, realm_list);
3399
0
      realm_list = NULL;
3400
0
    }
3401
0
    krb5_free_context(ctx);
3402
0
    ctx = NULL;
3403
0
  }
3404
0
  return realm;
3405
0
}
3406
3407
/**
3408
 * @brief Get an error string from a Kerberos error code.
3409
 *
3410
 * @param[in]  context  The library context.
3411
 *
3412
 * @param[in]  code     The Kerberos error code.
3413
 *
3414
 * @param[in]  mem_ctx  The talloc context to allocate the error string on.
3415
 *
3416
 * @return A talloc'ed error string or NULL if an error occurred.
3417
 *
3418
 * The caller must free the returned error string with talloc_free() if not
3419
 * needed anymore
3420
 */
3421
char *smb_get_krb5_error_message(krb5_context context,
3422
         krb5_error_code code,
3423
         TALLOC_CTX *mem_ctx)
3424
0
{
3425
0
  char *ret;
3426
3427
#if defined(HAVE_KRB5_GET_ERROR_MESSAGE) && defined(HAVE_KRB5_FREE_ERROR_MESSAGE)
3428
  const char *context_error = krb5_get_error_message(context, code);
3429
  if (context_error) {
3430
    ret = talloc_asprintf(mem_ctx, "%s: %s",
3431
          error_message(code), context_error);
3432
    krb5_free_error_message(context, context_error);
3433
    return ret;
3434
  }
3435
#endif
3436
0
  ret = talloc_strdup(mem_ctx, error_message(code));
3437
0
  return ret;
3438
0
}
3439
3440
/**
3441
 * @brief Return the type of a krb5_principal
3442
 *
3443
 * @param[in]  context  The library context.
3444
 *
3445
 * @param[in]  principal The principal to get the type from.
3446
 *
3447
 * @return The integer type of the principal.
3448
 */
3449
int smb_krb5_principal_get_type(krb5_context context,
3450
        krb5_const_principal principal)
3451
0
{
3452
0
#ifdef HAVE_KRB5_PRINCIPAL_GET_TYPE /* Heimdal */
3453
0
  return krb5_principal_get_type(context, principal);
3454
#elif defined(krb5_princ_type) /* MIT */
3455
  return krb5_princ_type(context, principal);
3456
#else
3457
#error  UNKNOWN_PRINC_GET_TYPE_FUNCTION
3458
#endif
3459
0
}
3460
3461
/**
3462
 * @brief Set the type of a principal
3463
 *
3464
 * @param[in]  context  The library context
3465
 *
3466
 * @param[inout] principal The principal to set the type for.
3467
 *
3468
 * @param[in]  type     The principal type to set.
3469
 */
3470
void smb_krb5_principal_set_type(krb5_context context,
3471
         krb5_principal principal,
3472
         int type)
3473
0
{
3474
0
#ifdef HAVE_KRB5_PRINCIPAL_SET_TYPE /* Heimdal */
3475
0
  krb5_principal_set_type(context, principal, type);
3476
#elif defined(krb5_princ_type) /* MIT */
3477
  krb5_princ_type(context, principal) = type;
3478
#else
3479
#error  UNKNOWN_PRINC_SET_TYPE_FUNCTION
3480
#endif
3481
0
}
3482
3483
/**
3484
 * @brief Check if a principal is a TGS
3485
 *
3486
 * @param[in]  context  The library context
3487
 *
3488
 * @param[inout] principal The principal to check.
3489
 *
3490
 * @returns 1 if equal, 0 if not and -1 on error.
3491
 */
3492
int smb_krb5_principal_is_tgs(krb5_context context,
3493
            krb5_const_principal principal)
3494
0
{
3495
0
  char *p = NULL;
3496
0
  int eq = 1;
3497
0
  krb5_error_code ret = 0;
3498
3499
0
  if (krb5_princ_size(context, principal) > 2) {
3500
0
    return 0;
3501
0
  }
3502
3503
0
  ret = smb_krb5_principal_get_comp_string(NULL, context, principal, 0, &p);
3504
0
  if (ret == ENOENT) {
3505
0
    return 0;
3506
0
  } else if (ret) {
3507
0
    return -1;
3508
0
  }
3509
3510
0
  eq = strcmp(p, KRB5_TGS_NAME) == 0;
3511
3512
0
  talloc_free(p);
3513
3514
0
  return eq;
3515
0
}
3516
3517
#if !defined(HAVE_KRB5_WARNX)
3518
/**
3519
 * @brief Log a Kerberos message
3520
 *
3521
 * It sends the message to com_err.
3522
 *
3523
 * @param[in]  context  The library context
3524
 *
3525
 * @param[in]  fmt      The message format
3526
 *
3527
 * @param[in]  ...      The message arguments
3528
 *
3529
 * @return 0 on success.
3530
 */
3531
krb5_error_code krb5_warnx(krb5_context context, const char *fmt, ...)
3532
{
3533
  va_list args;
3534
3535
  va_start(args, fmt);
3536
  com_err_va("samba-kdc", errno, fmt, args);
3537
  va_end(args);
3538
3539
  return 0;
3540
}
3541
#endif
3542
3543
/**
3544
 * @brief Copy a credential cache.
3545
 *
3546
 * @param[in]  context  The library context.
3547
 *
3548
 * @param[in]  incc     Credential cache to be copied.
3549
 *
3550
 * @param[inout] outcc  Copy of credential cache to be filled in.
3551
 *
3552
 * @return 0 on success, a Kerberos error code otherwise.
3553
 */
3554
krb5_error_code smb_krb5_cc_copy_creds(krb5_context context,
3555
               krb5_ccache incc, krb5_ccache outcc)
3556
0
{
3557
0
#ifdef HAVE_KRB5_CC_COPY_CACHE /* Heimdal */
3558
0
  return krb5_cc_copy_cache(context, incc, outcc);
3559
#elif defined(HAVE_KRB5_CC_COPY_CREDS)
3560
  krb5_error_code ret;
3561
  krb5_principal princ = NULL;
3562
3563
  ret = krb5_cc_get_principal(context, incc, &princ);
3564
  if (ret != 0) {
3565
    return ret;
3566
  }
3567
  ret = krb5_cc_initialize(context, outcc, princ);
3568
  krb5_free_principal(context, princ);
3569
  if (ret != 0) {
3570
    return ret;
3571
  }
3572
  return krb5_cc_copy_creds(context, incc, outcc);
3573
#else
3574
#error UNKNOWN_KRB5_CC_COPY_CACHE_OR_CREDS_FUNCTION
3575
#endif
3576
0
}
3577
3578
/**********************************************************
3579
 * ADS KRB5 CALLS
3580
 **********************************************************/
3581
3582
static bool ads_cleanup_expired_creds(krb5_context context,
3583
              krb5_ccache  ccache,
3584
              krb5_creds  *credsp)
3585
0
{
3586
0
  krb5_error_code retval;
3587
0
  const char *cc_type = krb5_cc_get_type(context, ccache);
3588
3589
0
  DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
3590
0
      cc_type, krb5_cc_get_name(context, ccache),
3591
0
      http_timestring(talloc_tos(), credsp->times.endtime)));
3592
3593
  /* we will probably need new tickets if the current ones
3594
     will expire within 10 seconds.
3595
  */
3596
0
  if (credsp->times.endtime >= (time(NULL) + 10))
3597
0
    return false;
3598
3599
  /* heimdal won't remove creds from a file ccache, and
3600
     perhaps we shouldn't anyway, since internally we
3601
     use memory ccaches, and a FILE one probably means that
3602
     we're using creds obtained outside of our executable
3603
  */
3604
0
  if (strequal(cc_type, "FILE")) {
3605
0
    DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
3606
0
    return false;
3607
0
  }
3608
3609
0
  retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
3610
0
  if (retval) {
3611
0
    DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
3612
0
        error_message(retval)));
3613
    /* If we have an error in this, we want to display it,
3614
       but continue as though we deleted it */
3615
0
  }
3616
0
  return true;
3617
0
}
3618
3619
/* Allocate and setup the auth context into the state we need. */
3620
3621
static krb5_error_code ads_setup_auth_context(krb5_context context,
3622
                krb5_auth_context *auth_context)
3623
0
{
3624
0
  krb5_error_code retval;
3625
3626
0
  retval = krb5_auth_con_init(context, auth_context );
3627
0
  if (retval) {
3628
0
    DEBUG(1,("krb5_auth_con_init failed (%s)\n",
3629
0
      error_message(retval)));
3630
0
    return retval;
3631
0
  }
3632
3633
  /* Ensure this is an addressless ticket. */
3634
0
  retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
3635
0
  if (retval) {
3636
0
    DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
3637
0
      error_message(retval)));
3638
0
  }
3639
3640
0
  return retval;
3641
0
}
3642
3643
#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
3644
static krb5_error_code ads_create_gss_checksum(krb5_data *in_data, /* [inout] */
3645
                 uint32_t gss_flags)
3646
{
3647
  unsigned int orig_length = in_data->length;
3648
  unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
3649
  char *gss_cksum = NULL;
3650
3651
  if (orig_length) {
3652
    /* Extra length field for delegated ticket. */
3653
    base_cksum_size += 4;
3654
  }
3655
3656
  if ((unsigned int)base_cksum_size + orig_length <
3657
      (unsigned int)base_cksum_size) {
3658
                return EINVAL;
3659
        }
3660
3661
  gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
3662
  if (gss_cksum == NULL) {
3663
    return ENOMEM;
3664
        }
3665
3666
  memset(gss_cksum, '\0', base_cksum_size + orig_length);
3667
  SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
3668
3669
  /*
3670
   * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
3671
   * This matches the behavior of heimdal and mit.
3672
   *
3673
   * And it is needed to work against some closed source
3674
   * SMB servers.
3675
   *
3676
   * See bug #7883
3677
   */
3678
  memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
3679
3680
  SIVAL(gss_cksum, 20, gss_flags);
3681
3682
  if (orig_length && in_data->data != NULL) {
3683
    SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
3684
    SSVAL(gss_cksum, 26, orig_length);
3685
    /* Copy the kerberos KRB_CRED data */
3686
    memcpy(gss_cksum + 28, in_data->data, orig_length);
3687
    free(in_data->data);
3688
    in_data->data = NULL;
3689
    in_data->length = 0;
3690
  }
3691
  in_data->data = gss_cksum;
3692
  in_data->length = base_cksum_size + orig_length;
3693
  return 0;
3694
}
3695
#endif
3696
3697
/*
3698
 * We can't use krb5_mk_req because w2k wants the service to be in a particular
3699
 * format.
3700
 */
3701
static krb5_error_code ads_krb5_mk_req(krb5_context context,
3702
               krb5_auth_context *auth_context,
3703
               const krb5_flags ap_req_options,
3704
               const char *principal,
3705
               krb5_ccache ccache,
3706
               krb5_data *outbuf,
3707
               time_t *expire_time,
3708
               const char *impersonate_princ_s)
3709
0
{
3710
0
  krb5_error_code retval;
3711
0
  krb5_principal server;
3712
0
  krb5_principal impersonate_princ = NULL;
3713
0
  krb5_creds *credsp;
3714
0
  krb5_creds creds;
3715
0
  krb5_data in_data;
3716
0
  bool creds_ready = false;
3717
0
  int i = 0, maxtries = 3;
3718
0
  bool ok;
3719
3720
0
  ZERO_STRUCT(in_data);
3721
3722
0
  retval = smb_krb5_parse_name(context, principal, &server);
3723
0
  if (retval != 0) {
3724
0
    DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
3725
0
    return retval;
3726
0
  }
3727
3728
0
  if (impersonate_princ_s) {
3729
0
    retval = smb_krb5_parse_name(context, impersonate_princ_s,
3730
0
               &impersonate_princ);
3731
0
    if (retval) {
3732
0
      DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", impersonate_princ_s));
3733
0
      goto cleanup_princ;
3734
0
    }
3735
0
  }
3736
3737
  /* obtain ticket & session key */
3738
0
  ZERO_STRUCT(creds);
3739
0
  if ((retval = krb5_copy_principal(context, server, &creds.server))) {
3740
0
    DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
3741
0
       error_message(retval)));
3742
0
    goto cleanup_princ;
3743
0
  }
3744
3745
0
  retval = krb5_cc_get_principal(context, ccache, &creds.client);
3746
0
  if (retval != 0) {
3747
    /* This can commonly fail on smbd startup with no ticket in the cache.
3748
     * Report at higher level than 1. */
3749
0
    DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
3750
0
       error_message(retval)));
3751
0
    goto cleanup_creds;
3752
0
  }
3753
3754
0
  while (!creds_ready && (i < maxtries)) {
3755
3756
0
    retval = smb_krb5_get_credentials(context,
3757
0
              ccache,
3758
0
              creds.client,
3759
0
              creds.server,
3760
0
              impersonate_princ,
3761
0
              &credsp);
3762
0
    if (retval != 0) {
3763
0
      DBG_WARNING("smb_krb5_get_credentials failed for %s "
3764
0
            "(%s)\n",
3765
0
            principal,
3766
0
            error_message(retval));
3767
0
      goto cleanup_creds;
3768
0
    }
3769
3770
    /* cope with ticket being in the future due to clock skew */
3771
0
    if ((unsigned)credsp->times.starttime > time(NULL)) {
3772
0
      time_t t = time(NULL);
3773
0
      int time_offset =(int)((unsigned)credsp->times.starttime-t);
3774
0
      DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
3775
0
      krb5_set_real_time(context, t + time_offset + 1, 0);
3776
0
    }
3777
3778
0
    ok = ads_cleanup_expired_creds(context, ccache, credsp);
3779
0
    if (!ok) {
3780
0
      creds_ready = true;
3781
0
    }
3782
3783
0
    i++;
3784
0
  }
3785
3786
0
  DBG_DEBUG("Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
3787
0
      principal,
3788
0
      krb5_cc_get_type(context, ccache),
3789
0
      krb5_cc_get_name(context, ccache),
3790
0
      http_timestring(talloc_tos(),
3791
0
          (unsigned)credsp->times.endtime),
3792
0
      (unsigned)credsp->times.endtime);
3793
3794
0
  if (expire_time) {
3795
0
    *expire_time = (time_t)credsp->times.endtime;
3796
0
  }
3797
3798
  /* Allocate the auth_context. */
3799
0
  retval = ads_setup_auth_context(context, auth_context);
3800
0
  if (retval != 0) {
3801
0
    DBG_WARNING("ads_setup_auth_context failed (%s)\n",
3802
0
          error_message(retval));
3803
0
    goto cleanup_creds;
3804
0
  }
3805
3806
#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
3807
  {
3808
    uint32_t gss_flags = 0;
3809
3810
    if (credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE) {
3811
      /*
3812
       * Fetch a forwarded TGT from the KDC so that we can
3813
       * hand off a 2nd ticket as part of the kerberos
3814
       * exchange.
3815
       */
3816
3817
      DBG_INFO("Server marked as OK to delegate to, building "
3818
         "forwardable TGT\n");
3819
3820
      retval = krb5_auth_con_setuseruserkey(context,
3821
          *auth_context,
3822
          &credsp->keyblock );
3823
      if (retval != 0) {
3824
        DBG_WARNING("krb5_auth_con_setuseruserkey "
3825
              "failed (%s)\n",
3826
              error_message(retval));
3827
        goto cleanup_creds;
3828
      }
3829
3830
      /* Must use a subkey for forwarded tickets. */
3831
      retval = krb5_auth_con_setflags(context,
3832
              *auth_context,
3833
              KRB5_AUTH_CONTEXT_USE_SUBKEY);
3834
      if (retval != 0) {
3835
        DBG_WARNING("krb5_auth_con_setflags failed (%s)\n",
3836
              error_message(retval));
3837
        goto cleanup_creds;
3838
      }
3839
3840
      retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
3841
        *auth_context,  /* Authentication context [in] */
3842
        discard_const_p(char, KRB5_TGS_NAME),  /* Ticket service name ("krbtgt") [in] */
3843
        credsp->client, /* Client principal for the tgt [in] */
3844
        credsp->server, /* Server principal for the tgt [in] */
3845
        ccache,         /* Credential cache to use for storage [in] */
3846
        1,              /* Turn on for "Forwardable ticket" [in] */
3847
        &in_data );     /* Resulting response [out] */
3848
3849
      if (retval) {
3850
        DBG_INFO("krb5_fwd_tgt_creds failed (%s)\n",
3851
           error_message(retval));
3852
3853
        /*
3854
         * This is not fatal. Delete the *auth_context and continue
3855
         * with krb5_mk_req_extended to get a non-forwardable ticket.
3856
         */
3857
3858
        if (in_data.data) {
3859
          free( in_data.data );
3860
          in_data.data = NULL;
3861
          in_data.length = 0;
3862
        }
3863
        krb5_auth_con_free(context, *auth_context);
3864
        *auth_context = NULL;
3865
        retval = ads_setup_auth_context(context, auth_context);
3866
        if (retval != 0) {
3867
          DBG_WARNING("ads_setup_auth_context failed (%s)\n",
3868
                error_message(retval));
3869
          goto cleanup_creds;
3870
        }
3871
      } else {
3872
        /* We got a delegated ticket. */
3873
        gss_flags |= GSS_C_DELEG_FLAG;
3874
      }
3875
    }
3876
3877
    /* Frees and reallocates in_data into a GSS checksum blob. */
3878
    retval = ads_create_gss_checksum(&in_data, gss_flags);
3879
    if (retval != 0) {
3880
      goto cleanup_data;
3881
    }
3882
3883
    /* We always want GSS-checksum types. */
3884
    retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
3885
    if (retval != 0) {
3886
      DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
3887
        error_message(retval)));
3888
      goto cleanup_data;
3889
    }
3890
  }
3891
#endif
3892
3893
0
  retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
3894
0
              &in_data, credsp, outbuf);
3895
0
  if (retval != 0) {
3896
0
    DBG_WARNING("krb5_mk_req_extended failed (%s)\n",
3897
0
          error_message(retval));
3898
0
  }
3899
3900
#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
3901
cleanup_data:
3902
#endif
3903
3904
0
  if (in_data.data) {
3905
0
    free( in_data.data );
3906
0
    in_data.length = 0;
3907
0
  }
3908
3909
0
  krb5_free_creds(context, credsp);
3910
3911
0
cleanup_creds:
3912
0
  krb5_free_cred_contents(context, &creds);
3913
3914
0
cleanup_princ:
3915
0
  krb5_free_principal(context, server);
3916
0
  if (impersonate_princ) {
3917
0
    krb5_free_principal(context, impersonate_princ);
3918
0
  }
3919
3920
0
  return retval;
3921
0
}
3922
3923
/*
3924
  get a kerberos5 ticket for the given service
3925
*/
3926
int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,
3927
          const char *principal,
3928
          time_t time_offset,
3929
          DATA_BLOB *ticket,
3930
          DATA_BLOB *session_key_krb5,
3931
          uint32_t extra_ap_opts, const char *ccname,
3932
          time_t *tgs_expire,
3933
          const char *impersonate_princ_s)
3934
0
{
3935
0
  krb5_error_code retval;
3936
0
  krb5_data packet;
3937
0
  krb5_context context = NULL;
3938
0
  krb5_ccache ccdef = NULL;
3939
0
  krb5_auth_context auth_context = NULL;
3940
0
  krb5_enctype enc_types[] = {
3941
0
    ENCTYPE_AES256_CTS_HMAC_SHA1_96,
3942
0
    ENCTYPE_AES128_CTS_HMAC_SHA1_96,
3943
0
    ENCTYPE_ARCFOUR_HMAC,
3944
0
    ENCTYPE_NULL};
3945
0
  bool ok;
3946
3947
0
  if (ccname == NULL) {
3948
0
    DBG_ERR("No explicit ccache given for service [%s], "
3949
0
      "impersonating [%s]\n",
3950
0
      principal, impersonate_princ_s);
3951
0
    retval = EINVAL;
3952
0
    goto failed;
3953
0
  }
3954
3955
0
  DBG_DEBUG("Getting ticket for service [%s] using creds from [%s] "
3956
0
      "and impersonating [%s]\n",
3957
0
      principal, ccname, impersonate_princ_s);
3958
3959
0
  retval = smb_krb5_init_context_common(&context);
3960
0
  if (retval != 0) {
3961
0
    DBG_ERR("kerberos init context failed (%s)\n",
3962
0
      error_message(retval));
3963
0
    goto failed;
3964
0
  }
3965
3966
0
  if (time_offset != 0) {
3967
0
    krb5_set_real_time(context, time(NULL) + time_offset, 0);
3968
0
  }
3969
3970
0
  retval = krb5_cc_resolve(context, ccname, &ccdef);
3971
0
  if (retval != 0) {
3972
0
    DBG_WARNING("krb5_cc_resolve(%s) failed (%s)\n",
3973
0
          ccname, error_message(retval));
3974
0
    goto failed;
3975
0
  }
3976
3977
0
  retval = krb5_set_default_tgs_ktypes(context, enc_types);
3978
0
  if (retval != 0) {
3979
0
    DBG_WARNING("krb5_set_default_tgs_ktypes failed (%s)\n",
3980
0
          error_message(retval));
3981
0
    goto failed;
3982
0
  }
3983
3984
0
  retval = ads_krb5_mk_req(context,
3985
0
         &auth_context,
3986
0
         AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
3987
0
         principal,
3988
0
         ccdef,
3989
0
         &packet,
3990
0
         tgs_expire,
3991
0
         impersonate_princ_s);
3992
0
  if (retval != 0) {
3993
0
    goto failed;
3994
0
  }
3995
3996
0
  ok = smb_krb5_get_smb_session_key(mem_ctx,
3997
0
            context,
3998
0
            auth_context,
3999
0
            session_key_krb5,
4000
0
            false);
4001
0
  if (!ok) {
4002
0
    retval = ENOMEM;
4003
0
    goto failed;
4004
0
  }
4005
4006
0
  *ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
4007
4008
0
  smb_krb5_free_data_contents(context, &packet);
4009
4010
0
failed:
4011
4012
0
  if (context) {
4013
0
    if (ccdef) {
4014
0
      krb5_cc_close(context, ccdef);
4015
0
    }
4016
0
    if (auth_context) {
4017
0
      krb5_auth_con_free(context, auth_context);
4018
0
    }
4019
0
    krb5_free_context(context);
4020
0
  }
4021
4022
0
  return retval;
4023
0
}
4024
4025
#ifndef SAMBA4_USES_HEIMDAL /* MITKRB5 tracing callback */
4026
static void smb_krb5_trace_cb(krb5_context ctx,
4027
#ifdef HAVE_KRB5_TRACE_INFO
4028
            const krb5_trace_info *info,
4029
#elif defined(HAVE_KRB5_TRACE_INFO_STRUCT)
4030
            const struct krb5_trace_info *info,
4031
#else
4032
#error unknown krb5_trace_info
4033
#endif
4034
            void *data)
4035
{
4036
  if (info != NULL) {
4037
    DBGC_DEBUG(DBGC_KERBEROS, "%s", info->message);
4038
  }
4039
}
4040
#endif
4041
4042
krb5_error_code smb_krb5_init_context_common(krb5_context *_krb5_context)
4043
0
{
4044
0
  krb5_error_code ret;
4045
0
  krb5_context krb5_ctx;
4046
4047
0
  initialize_krb5_error_table();
4048
4049
0
  ret = krb5_init_context(&krb5_ctx);
4050
0
  if (ret) {
4051
0
    DBG_ERR("Krb5 context initialization failed (%s)\n",
4052
0
       error_message(ret));
4053
0
    return ret;
4054
0
  }
4055
4056
  /* The MIT Kerberos build relies on using the system krb5.conf file.
4057
   * If you really want to use another file please set KRB5_CONFIG
4058
   * accordingly. */
4059
#ifndef SAMBA4_USES_HEIMDAL
4060
  ret = krb5_set_trace_callback(krb5_ctx, smb_krb5_trace_cb, NULL);
4061
  if (ret) {
4062
    DBG_ERR("Failed to set MIT kerberos trace callback! (%s)\n",
4063
      error_message(ret));
4064
  }
4065
#endif
4066
4067
0
#ifdef SAMBA4_USES_HEIMDAL
4068
  /* Set options in kerberos */
4069
0
  krb5_set_dns_canonicalize_hostname(krb5_ctx, false);
4070
0
#endif
4071
4072
0
  *_krb5_context = krb5_ctx;
4073
0
  return 0;
4074
0
}
4075
4076
/*
4077
 * This should only be used in code that
4078
 * really wants to touch the global default ccache!
4079
 */
4080
krb5_error_code smb_force_krb5_cc_default(krb5_context ctx, krb5_ccache *id)
4081
0
{
4082
0
#undef krb5_cc_default
4083
0
  return krb5_cc_default(ctx, id);
4084
0
#define krb5_cc_default __ERROR__XX__NEVER_USE_krb5_cc_default__;
4085
0
}
4086
4087
/*
4088
 * This should only be used in code that
4089
 * really wants to touch the global default ccache!
4090
 */
4091
const char *smb_force_krb5_cc_default_name(krb5_context ctx)
4092
0
{
4093
0
#undef krb5_cc_default_name
4094
0
  return krb5_cc_default_name(ctx);
4095
0
#define krb5_cc_default_name __ERROR__XX__NEVER_USE_krb5_cc_default_name__;
4096
0
}
4097
4098
#else /* HAVE_KRB5 */
4099
/* This saves a few linking headaches */
4100
int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,
4101
          const char *principal,
4102
          time_t time_offset,
4103
          DATA_BLOB *ticket,
4104
          DATA_BLOB *session_key_krb5,
4105
          uint32_t extra_ap_opts, const char *ccname,
4106
          time_t *tgs_expire,
4107
          const char *impersonate_princ_s)
4108
{
4109
   DEBUG(0,("NO KERBEROS SUPPORT\n"));
4110
   return 1;
4111
}
4112
4113
#endif /* HAVE_KRB5 */