Coverage Report

Created: 2025-07-23 07:04

/src/samba/third_party/heimdal/lib/krb5/ticket.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3
 * (Royal Institute of Technology, Stockholm, Sweden).
4
 * All rights reserved.
5
 *
6
 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 *
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 *
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * 3. Neither the name of the Institute nor the names of its contributors
20
 *    may be used to endorse or promote products derived from this software
21
 *    without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
 * SUCH DAMAGE.
34
 */
35
36
#include "krb5_locl.h"
37
38
/**
39
 * Free ticket and content
40
 *
41
 * @param context a Kerberos 5 context
42
 * @param ticket ticket to free
43
 *
44
 * @return Returns 0 to indicate success.  Otherwise an kerberos et
45
 * error code is returned, see krb5_get_error_message().
46
 *
47
 * @ingroup krb5
48
 */
49
50
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
51
krb5_free_ticket(krb5_context context,
52
     krb5_ticket *ticket)
53
0
{
54
0
    free_EncTicketPart(&ticket->ticket);
55
0
    krb5_free_principal(context, ticket->client);
56
0
    krb5_free_principal(context, ticket->server);
57
0
    free(ticket);
58
0
    return 0;
59
0
}
60
61
/**
62
 * Copy ticket and content
63
 *
64
 * @param context a Kerberos 5 context
65
 * @param from ticket to copy
66
 * @param to new copy of ticket, free with krb5_free_ticket()
67
 *
68
 * @return Returns 0 to indicate success.  Otherwise an kerberos et
69
 * error code is returned, see krb5_get_error_message().
70
 *
71
 * @ingroup krb5
72
 */
73
74
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
75
krb5_copy_ticket(krb5_context context,
76
     const krb5_ticket *from,
77
     krb5_ticket **to)
78
0
{
79
0
    krb5_error_code ret;
80
0
    krb5_ticket *tmp;
81
82
0
    *to = NULL;
83
0
    tmp = malloc(sizeof(*tmp));
84
0
    if (tmp == NULL)
85
0
  return krb5_enomem(context);
86
0
    if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
87
0
  free(tmp);
88
0
  return ret;
89
0
    }
90
0
    ret = krb5_copy_principal(context, from->client, &tmp->client);
91
0
    if(ret){
92
0
  free_EncTicketPart(&tmp->ticket);
93
0
  free(tmp);
94
0
  return ret;
95
0
    }
96
0
    ret = krb5_copy_principal(context, from->server, &tmp->server);
97
0
    if(ret){
98
0
  krb5_free_principal(context, tmp->client);
99
0
  free_EncTicketPart(&tmp->ticket);
100
0
  free(tmp);
101
0
  return ret;
102
0
    }
103
0
    *to = tmp;
104
0
    return 0;
105
0
}
106
107
/**
108
 * Return client principal in ticket
109
 *
110
 * @param context a Kerberos 5 context
111
 * @param ticket ticket to copy
112
 * @param client client principal, free with krb5_free_principal()
113
 *
114
 * @return Returns 0 to indicate success.  Otherwise an kerberos et
115
 * error code is returned, see krb5_get_error_message().
116
 *
117
 * @ingroup krb5
118
 */
119
120
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
121
krb5_ticket_get_client(krb5_context context,
122
           const krb5_ticket *ticket,
123
           krb5_principal *client)
124
0
{
125
0
    return krb5_copy_principal(context, ticket->client, client);
126
0
}
127
128
/**
129
 * Return server principal in ticket
130
 *
131
 * @param context a Kerberos 5 context
132
 * @param ticket ticket to copy
133
 * @param server server principal, free with krb5_free_principal()
134
 *
135
 * @return Returns 0 to indicate success.  Otherwise an kerberos et
136
 * error code is returned, see krb5_get_error_message().
137
 *
138
 * @ingroup krb5
139
 */
140
141
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
142
krb5_ticket_get_server(krb5_context context,
143
           const krb5_ticket *ticket,
144
           krb5_principal *server)
145
0
{
146
0
    return krb5_copy_principal(context, ticket->server, server);
147
0
}
148
149
/**
150
 * Return end time of a ticket
151
 *
152
 * @param context a Kerberos 5 context
153
 * @param ticket ticket to copy
154
 *
155
 * @return end time of ticket
156
 *
157
 * @ingroup krb5
158
 */
159
160
KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
161
krb5_ticket_get_endtime(krb5_context context,
162
      const krb5_ticket *ticket)
163
0
{
164
0
    return ticket->ticket.endtime;
165
0
}
166
167
/**
168
 * Return authentication, start, end, and renew limit times of a ticket
169
 *
170
 * @param context a Kerberos 5 context
171
 * @param ticket ticket to copy
172
 * @param t pointer to krb5_times structure
173
 *
174
 * @ingroup krb5
175
 */
176
177
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
178
krb5_ticket_get_times(krb5_context context,
179
                      const krb5_ticket *ticket,
180
                      krb5_times *t)
181
0
{
182
0
    t->authtime   = ticket->ticket.authtime;
183
0
    t->starttime  = ticket->ticket.starttime  ? *ticket->ticket.starttime  :
184
0
                                                t->authtime;
185
0
    t->endtime    = ticket->ticket.endtime;
186
0
    t->renew_till = ticket->ticket.renew_till ? *ticket->ticket.renew_till :
187
0
                                                t->endtime;
188
0
}
189
190
/**
191
 * Get the flags from the Kerberos ticket
192
 *
193
 * @param context Kerberos context
194
 * @param ticket Kerberos ticket
195
 *
196
 * @return ticket flags
197
 *
198
 * @ingroup krb5_ticket
199
 */
200
KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
201
krb5_ticket_get_flags(krb5_context context,
202
          const krb5_ticket *ticket)
203
0
{
204
0
    return TicketFlags2int(ticket->ticket.flags);
205
0
}
206
207
/*
208
 * Find an authz-data element in the given `ad'.  If `failp', then validate any
209
 * containing AD-KDC-ISSUED's keyed checksum with the `sessionkey' (if given).
210
 *
211
 * All AD-KDC-ISSUED will be validated (if requested) even when `type' is
212
 * `KRB5_AUTHDATA_KDC_ISSUED'.
213
 *
214
 * Only the first matching element will be output (via `data').
215
 *
216
 * Note that all AD-KDC-ISSUEDs found while traversing the authz-data will be
217
 * validated, though only the first one will be returned.
218
 *
219
 * XXX We really need a better interface though.  First, forget AD-AND-OR --
220
 * just remove it.  Second, probably forget AD-KDC-ISSUED, but still, between
221
 * that, the PAC, and the CAMMAC, we need an interface that can:
222
 *
223
 * a) take the derived keys instead of the service key or the session key,
224
 * b) can indicate whether the element was marked critical,
225
 * c) can indicate whether the element was authenticated to the KDC,
226
 * d) can iterate over all the instances found (if more than one is found).
227
 *
228
 * Also, we need to know here if the authz-data is from a Ticket or from an
229
 * Authenticator -- if the latter then we must refuse to find AD-KDC-ISSUED /
230
 * PAC / CAMMAC or anything of the sort, ever.
231
 */
232
static int
233
find_type_in_ad(krb5_context context,
234
    int type,
235
    krb5_data *data,                /* optional */
236
    krb5_boolean *found,
237
    krb5_boolean failp,             /* validate AD-KDC-ISSUED */
238
    krb5_keyblock *sessionkey,      /* ticket session key */
239
    const AuthorizationData *ad,
240
    int level)
241
0
{
242
0
    krb5_error_code ret = 0;
243
0
    size_t i;
244
245
0
    if (level > 9) {
246
0
  ret = ENOENT; /* XXX */
247
0
  krb5_set_error_message(context, ret,
248
0
             N_("Authorization data nested deeper "
249
0
          "then %d levels, stop searching", ""),
250
0
             level);
251
0
  goto out;
252
0
    }
253
254
    /*
255
     * Only copy out the element the first time we get to it, we need
256
     * to run over the whole authorization data fields to check if
257
     * there are any container clases we need to care about.
258
     */
259
0
    for (i = 0; i < ad->len; i++) {
260
0
  if (!*found && ad->val[i].ad_type == type) {
261
0
            if (data) {
262
0
                ret = der_copy_octet_string(&ad->val[i].ad_data, data);
263
0
                if (ret) {
264
0
                    krb5_set_error_message(context, ret,
265
0
                                           N_("malloc: out of memory", ""));
266
0
                    goto out;
267
0
                }
268
0
            }
269
0
      *found = TRUE;
270
0
            if (type != KRB5_AUTHDATA_KDC_ISSUED ||
271
0
                !failp || !sessionkey || !sessionkey->keyvalue.length)
272
0
                continue;
273
            /* else go on to validate the AD-KDC-ISSUED's keyed checksum */
274
0
  }
275
0
  switch (ad->val[i].ad_type) {
276
0
  case KRB5_AUTHDATA_IF_RELEVANT: {
277
0
      AuthorizationData child;
278
0
      ret = decode_AuthorizationData(ad->val[i].ad_data.data,
279
0
             ad->val[i].ad_data.length,
280
0
             &child,
281
0
             NULL);
282
0
      if (ret) {
283
0
    krb5_set_error_message(context, ret,
284
0
               N_("Failed to decode "
285
0
            "IF_RELEVANT with %d", ""),
286
0
               (int)ret);
287
0
    goto out;
288
0
      }
289
0
      ret = find_type_in_ad(context, type, data, found, FALSE,
290
0
          sessionkey, &child, level + 1);
291
0
      free_AuthorizationData(&child);
292
0
      if (ret)
293
0
    goto out;
294
0
      break;
295
0
  }
296
0
  case KRB5_AUTHDATA_KDC_ISSUED: {
297
0
      AD_KDCIssued child;
298
299
0
      ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
300
0
              ad->val[i].ad_data.length,
301
0
              &child,
302
0
              NULL);
303
0
      if (ret) {
304
0
    krb5_set_error_message(context, ret,
305
0
               N_("Failed to decode "
306
0
            "AD_KDCIssued with %d", ""),
307
0
               ret);
308
0
    goto out;
309
0
      }
310
0
      if (failp && sessionkey && sessionkey->keyvalue.length) {
311
0
    krb5_boolean valid;
312
0
    krb5_data buf;
313
0
    size_t len;
314
315
0
    ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
316
0
           &child.elements, &len, ret);
317
0
    if (ret) {
318
0
        free_AD_KDCIssued(&child);
319
0
        krb5_clear_error_message(context);
320
0
        goto out;
321
0
    }
322
0
    if(buf.length != len)
323
0
        krb5_abortx(context, "internal error in ASN.1 encoder");
324
325
0
    ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
326
0
               &child.ad_checksum, &valid);
327
0
    krb5_data_free(&buf);
328
0
    if (ret) {
329
0
        free_AD_KDCIssued(&child);
330
0
        goto out;
331
0
    }
332
0
    if (!valid) {
333
0
        krb5_clear_error_message(context);
334
0
        ret = ENOENT;
335
0
        free_AD_KDCIssued(&child);
336
0
        goto out;
337
0
    }
338
0
      } else if (failp) {
339
0
        krb5_clear_error_message(context);
340
0
        ret = ENOENT;
341
0
        free_AD_KDCIssued(&child);
342
0
        goto out;
343
0
            }
344
0
      ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
345
0
          &child.elements, level + 1);
346
0
      free_AD_KDCIssued(&child);
347
0
      if (ret)
348
0
    goto out;
349
0
      break;
350
0
  }
351
0
  case KRB5_AUTHDATA_AND_OR:
352
0
      if (!failp)
353
0
    break;
354
0
      ret = ENOENT; /* XXX */
355
0
      krb5_set_error_message(context, ret,
356
0
           N_("Authorization data contains "
357
0
              "AND-OR element that is unknown to the "
358
0
              "application", ""));
359
0
      goto out;
360
0
  default:
361
0
      if (!failp)
362
0
    break;
363
0
      ret = ENOENT; /* XXX */
364
0
      krb5_set_error_message(context, ret,
365
0
           N_("Authorization data contains "
366
0
              "unknown type (%d) ", ""),
367
0
           ad->val[i].ad_type);
368
0
      goto out;
369
0
  }
370
0
    }
371
0
out:
372
0
    if (ret) {
373
0
  if (*found) {
374
0
            if (data)
375
0
                krb5_data_free(data);
376
0
      *found = 0;
377
0
  }
378
0
    }
379
0
    return ret;
380
0
}
381
382
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
383
_krb5_get_ad(krb5_context context,
384
       const AuthorizationData *ad,
385
       krb5_keyblock *sessionkey,
386
       int type,
387
       krb5_data *data)
388
0
{
389
0
    krb5_boolean found = FALSE;
390
0
    krb5_error_code ret;
391
392
0
    if (data)
393
0
        krb5_data_zero(data);
394
395
0
    if (ad == NULL) {
396
0
  krb5_set_error_message(context, ENOENT,
397
0
             N_("No authorization data", ""));
398
0
  return ENOENT; /* XXX */
399
0
    }
400
401
0
    ret = find_type_in_ad(context, type, data, &found, TRUE, sessionkey, ad, 0);
402
0
    if (ret)
403
0
  return ret;
404
0
    if (!found) {
405
0
  krb5_set_error_message(context, ENOENT,
406
0
             N_("Have no authorization data of type %d", ""),
407
0
             type);
408
0
  return ENOENT; /* XXX */
409
0
    }
410
0
    return 0;
411
0
}
412
413
414
/**
415
 * Extract the authorization data type of type from the ticket. Store
416
 * the field in data. This function is to use for kerberos
417
 * applications.
418
 *
419
 * @param context a Kerberos 5 context
420
 * @param ticket Kerberos ticket
421
 * @param type type to fetch
422
 * @param data returned data, free with krb5_data_free()
423
 *
424
 * @ingroup krb5
425
 */
426
427
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
428
krb5_ticket_get_authorization_data_type(krb5_context context,
429
          krb5_ticket *ticket,
430
          int type,
431
          krb5_data *data)
432
0
{
433
0
    AuthorizationData *ad;
434
0
    krb5_error_code ret;
435
0
    krb5_boolean found = FALSE;
436
437
0
    if (data)
438
0
        krb5_data_zero(data);
439
440
0
    ad = ticket->ticket.authorization_data;
441
0
    if (ticket->ticket.authorization_data == NULL) {
442
0
  krb5_set_error_message(context, ENOENT,
443
0
             N_("Ticket has no authorization data", ""));
444
0
  return ENOENT; /* XXX */
445
0
    }
446
447
0
    ret = find_type_in_ad(context, type, data, &found, TRUE,
448
0
        &ticket->ticket.key, ad, 0);
449
0
    if (ret)
450
0
  return ret;
451
0
    if (!found) {
452
0
  krb5_set_error_message(context, ENOENT,
453
0
             N_("Ticket has no "
454
0
          "authorization data of type %d", ""),
455
0
             type);
456
0
  return ENOENT; /* XXX */
457
0
    }
458
0
    return 0;
459
0
}
460
461
static krb5_error_code
462
check_server_referral(krb5_context context,
463
          krb5_kdc_rep *rep,
464
          unsigned flags,
465
          krb5_const_principal requested,
466
          krb5_const_principal returned,
467
          krb5_keyblock * key)
468
0
{
469
0
    krb5_error_code ret;
470
0
    PA_ServerReferralData ref;
471
0
    krb5_crypto session;
472
0
    EncryptedData ed;
473
0
    size_t len;
474
0
    krb5_data data;
475
0
    PA_DATA *pa;
476
0
    int i = 0, cmp;
477
478
0
    if (rep->kdc_rep.padata == NULL)
479
0
  goto noreferral;
480
481
0
    pa = krb5_find_padata(rep->kdc_rep.padata->val,
482
0
        rep->kdc_rep.padata->len,
483
0
        KRB5_PADATA_SERVER_REFERRAL, &i);
484
0
    if (pa == NULL)
485
0
  goto noreferral;
486
487
0
    memset(&ed, 0, sizeof(ed));
488
0
    memset(&ref, 0, sizeof(ref));
489
490
0
    ret = decode_EncryptedData(pa->padata_value.data,
491
0
             pa->padata_value.length,
492
0
             &ed, &len);
493
0
    if (ret)
494
0
  return ret;
495
0
    if (len != pa->padata_value.length) {
496
0
  free_EncryptedData(&ed);
497
0
  krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
498
0
             N_("Referral EncryptedData wrong for realm %s",
499
0
          "realm"), requested->realm);
500
0
  return KRB5KRB_AP_ERR_MODIFIED;
501
0
    }
502
503
0
    ret = krb5_crypto_init(context, key, 0, &session);
504
0
    if (ret) {
505
0
  free_EncryptedData(&ed);
506
0
  return ret;
507
0
    }
508
509
0
    ret = krb5_decrypt_EncryptedData(context, session,
510
0
             KRB5_KU_PA_SERVER_REFERRAL,
511
0
             &ed, &data);
512
0
    free_EncryptedData(&ed);
513
0
    krb5_crypto_destroy(context, session);
514
0
    if (ret)
515
0
  return ret;
516
517
0
    ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
518
0
    if (ret) {
519
0
  krb5_data_free(&data);
520
0
  return ret;
521
0
    }
522
0
    krb5_data_free(&data);
523
524
0
    if (strcmp(requested->realm, returned->realm) != 0) {
525
0
  free_PA_ServerReferralData(&ref);
526
0
  krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
527
0
             N_("server ref realm mismatch, "
528
0
          "requested realm %s got back %s", ""),
529
0
             requested->realm, returned->realm);
530
0
  return KRB5KRB_AP_ERR_MODIFIED;
531
0
    }
532
533
0
    if (krb5_principal_is_krbtgt(context, returned)) {
534
0
  const char *realm = returned->name.name_string.val[1];
535
536
0
  if (ref.referred_realm == NULL
537
0
      || strcmp(*ref.referred_realm, realm) != 0)
538
0
  {
539
0
      free_PA_ServerReferralData(&ref);
540
0
      krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
541
0
           N_("tgt returned with wrong ref", ""));
542
0
      return KRB5KRB_AP_ERR_MODIFIED;
543
0
  }
544
0
    } else if (krb5_principal_compare(context, returned, requested) == 0) {
545
0
  free_PA_ServerReferralData(&ref);
546
0
  krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
547
0
             N_("req princ no same as returned", ""));
548
0
  return KRB5KRB_AP_ERR_MODIFIED;
549
0
    }
550
551
0
    if (ref.requested_principal_name) {
552
0
  cmp = _krb5_principal_compare_PrincipalName(context,
553
0
                requested,
554
0
                ref.requested_principal_name);
555
0
  if (!cmp) {
556
0
      free_PA_ServerReferralData(&ref);
557
0
      krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
558
0
           N_("referred principal not same "
559
0
              "as requested", ""));
560
0
      return KRB5KRB_AP_ERR_MODIFIED;
561
0
  }
562
0
    } else if (flags & EXTRACT_TICKET_AS_REQ) {
563
0
  free_PA_ServerReferralData(&ref);
564
0
  krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
565
0
             N_("Requested principal missing on AS-REQ", ""));
566
0
  return KRB5KRB_AP_ERR_MODIFIED;
567
0
    }
568
569
0
    free_PA_ServerReferralData(&ref);
570
571
0
    return ret;
572
0
noreferral:
573
    /*
574
     * Expect excact match or that we got a krbtgt
575
     */
576
0
    if (krb5_principal_compare(context, requested, returned) != TRUE &&
577
0
  (krb5_realm_compare(context, requested, returned) != TRUE &&
578
0
   krb5_principal_is_krbtgt(context, returned) != TRUE))
579
0
    {
580
0
  krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
581
0
             N_("Not same server principal returned "
582
0
          "as requested", ""));
583
0
  return KRB5KRB_AP_ERR_MODIFIED;
584
0
    }
585
0
    return 0;
586
0
}
587
588
/*
589
 * Verify KDC supported anonymous if requested
590
 */
591
static krb5_error_code
592
check_client_anonymous(krb5_context context,
593
           krb5_kdc_rep *rep,
594
           krb5_const_principal requested,
595
           krb5_const_principal mapped,
596
           krb5_boolean is_tgs_rep)
597
0
{
598
0
    int flags;
599
600
0
    if (!rep->enc_part.flags.anonymous)
601
0
  return KRB5KDC_ERR_BADOPTION;
602
603
    /*
604
     * Here we must validate that the AS returned a ticket of the expected type
605
     * for either a fully anonymous request, or authenticated request for an
606
     * anonymous ticket.  If this is a TGS request, we're done.  Then if the
607
     * 'requested' principal was anonymous, we'll check the 'mapped' principal
608
     * accordingly (without enforcing the name type and perhaps the realm).
609
     * Finally, if the 'requested' principal was not anonymous, well check
610
     * that the 'mapped' principal has an anonymous name and type, in a
611
     * non-anonymous realm.  (Should we also be checking for a realm match
612
     * between the request and the mapped name in this case?)
613
     */
614
0
    if (is_tgs_rep)
615
0
  flags = KRB5_ANON_MATCH_ANY_NONT;
616
0
    else if (krb5_principal_is_anonymous(context, requested,
617
0
                                         KRB5_ANON_MATCH_ANY_NONT))
618
0
  flags = KRB5_ANON_MATCH_UNAUTHENTICATED | KRB5_ANON_IGNORE_NAME_TYPE;
619
0
    else
620
0
  flags = KRB5_ANON_MATCH_AUTHENTICATED;
621
622
0
    if (!krb5_principal_is_anonymous(context, mapped, flags))
623
0
  return KRB5KRB_AP_ERR_MODIFIED;
624
625
0
    return 0;
626
0
}
627
628
/*
629
 * Verify returned client principal name in anonymous/referral case
630
 */
631
632
static krb5_error_code
633
check_client_mismatch(krb5_context context,
634
          krb5_kdc_rep *rep,
635
          krb5_const_principal requested,
636
          krb5_const_principal mapped,
637
          krb5_keyblock const * key)
638
0
{
639
0
    if (rep->enc_part.flags.anonymous) {
640
0
  if (!krb5_principal_is_anonymous(context, mapped,
641
0
                                         KRB5_ANON_MATCH_ANY_NONT)) {
642
0
      krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
643
0
           N_("Anonymous ticket does not contain anonymous "
644
0
              "principal", ""));
645
0
      return KRB5KRB_AP_ERR_MODIFIED;
646
0
  }
647
0
    } else {
648
0
  if (krb5_principal_compare(context, requested, mapped) == FALSE &&
649
0
      !rep->enc_part.flags.enc_pa_rep) {
650
0
      krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
651
0
           N_("Not same client principal returned "
652
0
           "as requested", ""));
653
0
      return KRB5KRB_AP_ERR_MODIFIED;
654
0
  }
655
0
    }
656
657
0
    return 0;
658
0
}
659
660
661
static krb5_error_code KRB5_CALLCONV
662
decrypt_tkt (krb5_context context,
663
       krb5_keyblock *key,
664
       krb5_key_usage usage,
665
       krb5_const_pointer decrypt_arg,
666
       krb5_kdc_rep *dec_rep)
667
0
{
668
0
    krb5_error_code ret;
669
0
    krb5_data data;
670
0
    size_t size;
671
0
    krb5_crypto crypto;
672
673
0
    ret = krb5_crypto_init(context, key, 0, &crypto);
674
0
    if (ret)
675
0
  return ret;
676
677
0
    ret = krb5_decrypt_EncryptedData (context,
678
0
              crypto,
679
0
              usage,
680
0
              &dec_rep->kdc_rep.enc_part,
681
0
              &data);
682
0
    krb5_crypto_destroy(context, crypto);
683
684
0
    if (ret)
685
0
  return ret;
686
687
0
    ret = decode_EncASRepPart(data.data,
688
0
            data.length,
689
0
            &dec_rep->enc_part,
690
0
            &size);
691
0
    if (ret)
692
0
  ret = decode_EncTGSRepPart(data.data,
693
0
           data.length,
694
0
           &dec_rep->enc_part,
695
0
           &size);
696
0
    krb5_data_free (&data);
697
0
    if (ret) {
698
0
        krb5_set_error_message(context, ret,
699
0
             N_("Failed to decode encpart in ticket", ""));
700
0
  return ret;
701
0
    }
702
0
    return 0;
703
0
}
704
705
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
706
_krb5_extract_ticket(krb5_context context,
707
         krb5_kdc_rep *rep,
708
         krb5_creds *creds,
709
         krb5_keyblock *key,
710
         krb5_const_pointer keyseed,
711
         krb5_key_usage key_usage,
712
         krb5_addresses *addrs,
713
         unsigned nonce,
714
         unsigned flags,
715
         krb5_data *request,
716
         krb5_decrypt_proc decrypt_proc,
717
         krb5_const_pointer decryptarg)
718
0
{
719
0
    krb5_error_code ret;
720
0
    krb5_principal tmp_principal;
721
0
    size_t len = 0;
722
0
    time_t tmp_time;
723
0
    krb5_timestamp sec_now;
724
725
    /* decrypt */
726
727
0
    if (decrypt_proc == NULL)
728
0
  decrypt_proc = decrypt_tkt;
729
730
0
    ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
731
0
    if (ret)
732
0
  goto out;
733
734
0
    if (rep->enc_part.flags.enc_pa_rep && request) {
735
0
  krb5_crypto crypto = NULL;
736
0
  Checksum cksum;
737
0
  PA_DATA *pa = NULL;
738
0
  int idx = 0;
739
740
0
  _krb5_debug(context, 5, "processing enc-ap-rep");
741
742
0
  if (rep->enc_part.encrypted_pa_data == NULL ||
743
0
      (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val,
744
0
           rep->enc_part.encrypted_pa_data->len,
745
0
           KRB5_PADATA_REQ_ENC_PA_REP,
746
0
           &idx)) == NULL)
747
0
  {
748
0
      _krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
749
0
      ret = KRB5KRB_AP_ERR_MODIFIED;
750
0
      goto out;
751
0
  }
752
  
753
0
  ret = krb5_crypto_init(context, key, 0, &crypto);
754
0
  if (ret)
755
0
      goto out;
756
  
757
0
  ret = decode_Checksum(pa->padata_value.data,
758
0
            pa->padata_value.length,
759
0
            &cksum, NULL);
760
0
  if (ret) {
761
0
      krb5_crypto_destroy(context, crypto);
762
0
      goto out;
763
0
  }
764
  
765
0
  ret = krb5_verify_checksum(context, crypto,
766
0
           KRB5_KU_AS_REQ,
767
0
           request->data, request->length,
768
0
           &cksum);
769
0
  krb5_crypto_destroy(context, crypto);
770
0
  free_Checksum(&cksum);
771
0
  _krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in");
772
0
  if (ret)
773
0
      goto out;
774
0
    }
775
776
    /* save session key */
777
778
0
    creds->session.keyvalue.length = 0;
779
0
    creds->session.keyvalue.data   = NULL;
780
0
    creds->session.keytype = rep->enc_part.key.keytype;
781
0
    ret = krb5_data_copy (&creds->session.keyvalue,
782
0
        rep->enc_part.key.keyvalue.data,
783
0
        rep->enc_part.key.keyvalue.length);
784
0
    if (ret) {
785
0
  krb5_clear_error_message(context);
786
0
  goto out;
787
0
    }
788
789
    /* compare client and save */
790
0
    ret = _krb5_principalname2krb5_principal(context,
791
0
                                             &tmp_principal,
792
0
                                             rep->kdc_rep.cname,
793
0
                                             rep->kdc_rep.crealm);
794
0
    if (ret)
795
0
  goto out;
796
797
    /* check KDC supported anonymous if it was requested */
798
0
    if (flags & EXTRACT_TICKET_MATCH_ANON) {
799
0
  ret = check_client_anonymous(context,rep,
800
0
             creds->client,
801
0
             tmp_principal,
802
0
             request == NULL); /* is TGS */
803
0
  if (ret) {
804
0
      krb5_free_principal(context, tmp_principal);
805
0
      goto out;
806
0
  }
807
0
    }
808
809
    /* check client referral and save principal */
810
0
    if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
811
0
  ret = check_client_mismatch(context, rep,
812
0
            creds->client,
813
0
            tmp_principal,
814
0
            &creds->session);
815
0
  if (ret) {
816
0
      krb5_free_principal (context, tmp_principal);
817
0
      goto out;
818
0
  }
819
0
    }
820
0
    krb5_free_principal (context, creds->client);
821
0
    creds->client = tmp_principal;
822
823
    /* check server referral and save principal */
824
0
    ret = _krb5_kdcrep2krb5_principal(context, &tmp_principal, &rep->enc_part);
825
0
    if (ret)
826
0
  goto out;
827
828
0
    tmp_principal->nameattrs->peer_realm =
829
0
        calloc(1, sizeof(tmp_principal->nameattrs->peer_realm[0]));
830
0
    if (tmp_principal->nameattrs->peer_realm == NULL) {
831
0
        ret = krb5_enomem(context);
832
0
        goto out;
833
0
    }
834
0
    ret = copy_Realm(&creds->client->realm, tmp_principal->nameattrs->peer_realm);
835
0
    if (ret) goto out;
836
837
0
    if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
838
0
  ret = check_server_referral(context,
839
0
            rep,
840
0
            flags,
841
0
            creds->server,
842
0
            tmp_principal,
843
0
            &creds->session);
844
0
  if (ret) {
845
0
      krb5_free_principal (context, tmp_principal);
846
0
      goto out;
847
0
  }
848
0
    }
849
0
    krb5_free_principal(context, creds->server);
850
0
    creds->server = tmp_principal;
851
852
    /* verify names */
853
0
    if(flags & EXTRACT_TICKET_MATCH_REALM){
854
0
  const char *srealm = krb5_principal_get_realm(context, creds->server);
855
0
  const char *crealm = krb5_principal_get_realm(context, creds->client);
856
857
0
  if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
858
0
      strcmp(rep->enc_part.srealm, crealm) != 0)
859
0
  {
860
0
      ret = KRB5KRB_AP_ERR_MODIFIED;
861
0
      krb5_clear_error_message(context);
862
0
      goto out;
863
0
  }
864
0
    }
865
866
    /* compare nonces */
867
868
0
    if (nonce != (unsigned)rep->enc_part.nonce) {
869
0
  ret = KRB5KRB_AP_ERR_MODIFIED;
870
0
  krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
871
0
  goto out;
872
0
    }
873
874
    /* set kdc-offset */
875
876
0
    krb5_timeofday (context, &sec_now);
877
0
    if (rep->enc_part.flags.initial
878
0
  && (flags & EXTRACT_TICKET_TIMESYNC)
879
0
  && context->kdc_sec_offset == 0
880
0
  && krb5_config_get_bool (context, NULL,
881
0
         "libdefaults",
882
0
         "kdc_timesync",
883
0
         NULL)) {
884
0
  context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
885
0
  krb5_timeofday (context, &sec_now);
886
0
    }
887
888
    /* check all times */
889
890
0
    if (rep->enc_part.starttime) {
891
0
  tmp_time = *rep->enc_part.starttime;
892
0
    } else
893
0
  tmp_time = rep->enc_part.authtime;
894
895
0
    if (creds->times.starttime == 0
896
0
  && krb5_time_abs(tmp_time, sec_now) > context->max_skew) {
897
0
  ret = KRB5KRB_AP_ERR_SKEW;
898
0
  krb5_set_error_message (context, ret,
899
0
        N_("time skew (%ld) larger than max (%ld)", ""),
900
0
             (long)krb5_time_abs(tmp_time, sec_now),
901
0
             (long)context->max_skew);
902
0
  goto out;
903
0
    }
904
905
0
    if (creds->times.starttime != 0
906
0
  && tmp_time != creds->times.starttime) {
907
0
  krb5_clear_error_message (context);
908
0
  ret = KRB5KRB_AP_ERR_MODIFIED;
909
0
  goto out;
910
0
    }
911
912
0
    creds->times.starttime = tmp_time;
913
914
0
    if (rep->enc_part.renew_till) {
915
0
  tmp_time = *rep->enc_part.renew_till;
916
0
    } else
917
0
  tmp_time = 0;
918
919
0
    if (creds->times.renew_till != 0
920
0
  && tmp_time > creds->times.renew_till) {
921
0
  krb5_clear_error_message (context);
922
0
  ret = KRB5KRB_AP_ERR_MODIFIED;
923
0
  goto out;
924
0
    }
925
926
0
    creds->times.renew_till = tmp_time;
927
928
0
    creds->times.authtime = rep->enc_part.authtime;
929
930
0
    if (creds->times.endtime != 0
931
0
  && rep->enc_part.endtime > creds->times.endtime) {
932
0
  krb5_clear_error_message (context);
933
0
  ret = KRB5KRB_AP_ERR_MODIFIED;
934
0
  goto out;
935
0
    }
936
937
0
    creds->times.endtime  = rep->enc_part.endtime;
938
939
0
    if(rep->enc_part.caddr)
940
0
  krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
941
0
    else if(addrs)
942
0
  krb5_copy_addresses (context, addrs, &creds->addresses);
943
0
    else {
944
0
  creds->addresses.len = 0;
945
0
  creds->addresses.val = NULL;
946
0
    }
947
0
    creds->flags.b = rep->enc_part.flags;
948
949
0
    creds->authdata.len = 0;
950
0
    creds->authdata.val = NULL;
951
952
    /* extract ticket */
953
0
    ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
954
0
           &rep->kdc_rep.ticket, &len, ret);
955
0
    if(ret)
956
0
  goto out;
957
0
    if (creds->ticket.length != len)
958
0
  krb5_abortx(context, "internal error in ASN.1 encoder");
959
0
    creds->second_ticket.length = 0;
960
0
    creds->second_ticket.data   = NULL;
961
962
963
0
out:
964
0
    memset (rep->enc_part.key.keyvalue.data, 0,
965
0
      rep->enc_part.key.keyvalue.length);
966
0
    return ret;
967
0
}