Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/sspi/Kerberos/krb5glue_mit.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Client
3
 * Kerberos Auth Protocol
4
 *
5
 * Copyright 2022 Isaac Klein <fifthdegree@protonmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 * http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#ifndef WITH_KRB5_MIT
21
#error "This file must only be included with MIT kerberos"
22
#endif
23
24
#include <string.h>
25
26
#include <winpr/path.h>
27
#include <winpr/wlog.h>
28
#include <winpr/endian.h>
29
#include <winpr/crypto.h>
30
#include <winpr/print.h>
31
#include <winpr/assert.h>
32
#include <errno.h>
33
#include "krb5glue.h"
34
#include <profile.h>
35
36
static char* create_temporary_file(void)
37
0
{
38
0
  BYTE buffer[32];
39
0
  char* hex = NULL;
40
0
  char* path = NULL;
41
42
0
  winpr_RAND(buffer, sizeof(buffer));
43
0
  hex = winpr_BinToHexString(buffer, sizeof(buffer), FALSE);
44
0
  path = GetKnownSubPath(KNOWN_PATH_TEMP, hex);
45
0
  free(hex);
46
0
  return path;
47
0
}
48
49
void krb5glue_keys_free(krb5_context ctx, struct krb5glue_keyset* keyset)
50
0
{
51
0
  WINPR_ASSERT(ctx);
52
0
  WINPR_ASSERT(keyset);
53
54
0
  krb5_k_free_key(ctx, keyset->session_key);
55
0
  krb5_k_free_key(ctx, keyset->initiator_key);
56
0
  krb5_k_free_key(ctx, keyset->acceptor_key);
57
0
}
58
59
krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_ctx, BOOL acceptor,
60
                                       struct krb5glue_keyset* keyset)
61
0
{
62
0
  WINPR_ASSERT(ctx);
63
0
  WINPR_ASSERT(auth_ctx);
64
0
  WINPR_ASSERT(keyset);
65
66
0
  krb5glue_keys_free(ctx, keyset);
67
0
  krb5_auth_con_getkey_k(ctx, auth_ctx, &keyset->session_key);
68
0
  if (acceptor)
69
0
  {
70
0
    krb5_auth_con_getsendsubkey_k(ctx, auth_ctx, &keyset->acceptor_key);
71
0
    krb5_auth_con_getrecvsubkey_k(ctx, auth_ctx, &keyset->initiator_key);
72
0
  }
73
0
  else
74
0
  {
75
0
    krb5_auth_con_getsendsubkey_k(ctx, auth_ctx, &keyset->initiator_key);
76
0
    krb5_auth_con_getrecvsubkey_k(ctx, auth_ctx, &keyset->acceptor_key);
77
0
  }
78
0
  return 0;
79
0
}
80
81
krb5_prompt_type krb5glue_get_prompt_type(krb5_context ctx, krb5_prompt prompts[], int index)
82
0
{
83
0
  WINPR_ASSERT(ctx);
84
0
  WINPR_ASSERT(prompts);
85
0
  WINPR_UNUSED(prompts);
86
87
0
  krb5_prompt_type* types = krb5_get_prompt_types(ctx);
88
0
  return types ? types[index] : 0;
89
0
}
90
91
krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag)
92
0
{
93
0
  krb5_error* error = NULL;
94
0
  krb5_error_code rv = 0;
95
96
0
  WINPR_ASSERT(ctx);
97
0
  WINPR_ASSERT(msg);
98
0
  WINPR_ASSERT(tag);
99
100
0
  if (!(rv = krb5_rd_error(ctx, msg, &error)))
101
0
  {
102
0
    WLog_ERR(tag, "KRB_ERROR: %s", error->text.data);
103
0
    krb5_free_error(ctx, error);
104
0
  }
105
106
0
  return rv;
107
0
}
108
109
BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype,
110
                                            uint32_t* flags)
111
0
{
112
0
  WINPR_ASSERT(flags);
113
114
0
  if (!authenticator || !authenticator->checksum ||
115
0
      authenticator->checksum->checksum_type != cksumtype || authenticator->checksum->length < 24)
116
0
    return FALSE;
117
0
  *flags = winpr_Data_Get_UINT32((authenticator->checksum->contents + 20));
118
0
  return TRUE;
119
0
}
120
121
krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache,
122
                                        krb5_prompter_fct prompter, char* password,
123
                                        SEC_WINPR_KERBEROS_SETTINGS* krb_settings)
124
0
{
125
0
  krb5_error_code rv = 0;
126
0
  krb5_deltat start_time = 0;
127
0
  krb5_get_init_creds_opt* gic_opt = NULL;
128
0
  krb5_init_creds_context creds_ctx = NULL;
129
0
  char* tmp_profile_path = create_temporary_file();
130
0
  profile_t profile = NULL;
131
0
  BOOL is_temp_ctx = FALSE;
132
133
0
  WINPR_ASSERT(ctx);
134
135
0
  rv = krb5_get_init_creds_opt_alloc(ctx, &gic_opt);
136
0
  if (rv)
137
0
    goto cleanup;
138
139
0
  krb5_get_init_creds_opt_set_forwardable(gic_opt, 0);
140
0
  krb5_get_init_creds_opt_set_proxiable(gic_opt, 0);
141
142
0
  if (krb_settings)
143
0
  {
144
0
    if (krb_settings->startTime)
145
0
      start_time = krb_settings->startTime;
146
0
    if (krb_settings->lifeTime)
147
0
      krb5_get_init_creds_opt_set_tkt_life(gic_opt, krb_settings->lifeTime);
148
0
    if (krb_settings->renewLifeTime)
149
0
      krb5_get_init_creds_opt_set_renew_life(gic_opt, krb_settings->renewLifeTime);
150
0
    if (krb_settings->withPac)
151
0
    {
152
0
      rv = krb5_get_init_creds_opt_set_pac_request(ctx, gic_opt, TRUE);
153
0
      if (rv)
154
0
        goto cleanup;
155
0
    }
156
0
    if (krb_settings->armorCache)
157
0
    {
158
0
      rv = krb5_get_init_creds_opt_set_fast_ccache_name(ctx, gic_opt,
159
0
                                                        krb_settings->armorCache);
160
0
      if (rv)
161
0
        goto cleanup;
162
0
    }
163
0
    if (krb_settings->pkinitX509Identity)
164
0
    {
165
0
      rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_user_identity",
166
0
                                          krb_settings->pkinitX509Identity);
167
0
      if (rv)
168
0
        goto cleanup;
169
0
    }
170
0
    if (krb_settings->pkinitX509Anchors)
171
0
    {
172
0
      rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_anchors",
173
0
                                          krb_settings->pkinitX509Anchors);
174
0
      if (rv)
175
0
        goto cleanup;
176
0
    }
177
0
    if (krb_settings->kdcUrl && (strnlen(krb_settings->kdcUrl, 2) > 0))
178
0
    {
179
0
      const char* names[4] = { 0 };
180
0
      char* realm = NULL;
181
0
      char* kdc_url = NULL;
182
0
      size_t size = 0;
183
184
0
      if ((rv = krb5_get_profile(ctx, &profile)))
185
0
        goto cleanup;
186
187
0
      rv = ENOMEM;
188
0
      if (winpr_asprintf(&kdc_url, &size, "https://%s/KdcProxy", krb_settings->kdcUrl) <= 0)
189
0
      {
190
0
        free(kdc_url);
191
0
        goto cleanup;
192
0
      }
193
194
0
      realm = calloc(princ->realm.length + 1, 1);
195
0
      if (!realm)
196
0
      {
197
0
        free(kdc_url);
198
0
        goto cleanup;
199
0
      }
200
0
      CopyMemory(realm, princ->realm.data, princ->realm.length);
201
202
0
      names[0] = "realms";
203
0
      names[1] = realm;
204
0
      names[2] = "kdc";
205
206
0
      profile_clear_relation(profile, names);
207
0
      profile_add_relation(profile, names, kdc_url);
208
209
      /* Since we know who the KDC is, tell krb5 that its certificate is valid for pkinit */
210
0
      names[2] = "pkinit_kdc_hostname";
211
0
      profile_add_relation(profile, names, krb_settings->kdcUrl);
212
213
0
      free(kdc_url);
214
0
      free(realm);
215
216
0
      long lrv = profile_flush_to_file(profile, tmp_profile_path);
217
0
      if (lrv)
218
0
        goto cleanup;
219
220
0
      profile_abandon(profile);
221
0
      profile = NULL;
222
0
      lrv = profile_init_path(tmp_profile_path, &profile);
223
0
      if (lrv)
224
0
        goto cleanup;
225
226
0
      rv = krb5_init_context_profile(profile, 0, &ctx);
227
0
      if (rv)
228
0
        goto cleanup;
229
0
      is_temp_ctx = TRUE;
230
0
    }
231
0
  }
232
233
0
  if ((rv = krb5_get_init_creds_opt_set_in_ccache(ctx, gic_opt, ccache)))
234
0
    goto cleanup;
235
236
0
  if ((rv = krb5_get_init_creds_opt_set_out_ccache(ctx, gic_opt, ccache)))
237
0
    goto cleanup;
238
239
0
  if ((rv =
240
0
           krb5_init_creds_init(ctx, princ, prompter, password, start_time, gic_opt, &creds_ctx)))
241
0
    goto cleanup;
242
243
0
  if ((rv = krb5_init_creds_get(ctx, creds_ctx)))
244
0
    goto cleanup;
245
246
0
cleanup:
247
0
  krb5_init_creds_free(ctx, creds_ctx);
248
0
  krb5_get_init_creds_opt_free(ctx, gic_opt);
249
0
  if (is_temp_ctx)
250
0
    krb5_free_context(ctx);
251
0
  profile_abandon(profile);
252
0
  winpr_DeleteFile(tmp_profile_path);
253
0
  free(tmp_profile_path);
254
255
0
  return rv;
256
0
}
257