Coverage Report

Created: 2024-09-08 06:20

/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
86
0
  krb5_prompt_type* types = krb5_get_prompt_types(ctx);
87
0
  return types ? types[index] : 0;
88
0
}
89
90
krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag)
91
0
{
92
0
  krb5_error* error = NULL;
93
0
  krb5_error_code rv = 0;
94
95
0
  WINPR_ASSERT(ctx);
96
0
  WINPR_ASSERT(msg);
97
0
  WINPR_ASSERT(tag);
98
99
0
  if (!(rv = krb5_rd_error(ctx, msg, &error)))
100
0
  {
101
0
    WLog_ERR(tag, "KRB_ERROR: %s", error->text.data);
102
0
    krb5_free_error(ctx, error);
103
0
  }
104
105
0
  return rv;
106
0
}
107
108
BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype,
109
                                            uint32_t* flags)
110
0
{
111
0
  WINPR_ASSERT(flags);
112
113
0
  if (!authenticator || !authenticator->checksum ||
114
0
      authenticator->checksum->checksum_type != cksumtype || authenticator->checksum->length < 24)
115
0
    return FALSE;
116
0
  Data_Read_UINT32((authenticator->checksum->contents + 20), (*flags));
117
0
  return TRUE;
118
0
}
119
120
krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache,
121
                                        krb5_prompter_fct prompter, char* password,
122
                                        SEC_WINPR_KERBEROS_SETTINGS* krb_settings)
123
0
{
124
0
  krb5_error_code rv = 0;
125
0
  krb5_deltat start_time = 0;
126
0
  krb5_get_init_creds_opt* gic_opt = NULL;
127
0
  krb5_init_creds_context creds_ctx = NULL;
128
0
  char* tmp_profile_path = create_temporary_file();
129
0
  profile_t profile = NULL;
130
0
  BOOL is_temp_ctx = FALSE;
131
132
0
  WINPR_ASSERT(ctx);
133
134
0
  rv = krb5_get_init_creds_opt_alloc(ctx, &gic_opt);
135
0
  if (rv)
136
0
    goto cleanup;
137
138
0
  krb5_get_init_creds_opt_set_forwardable(gic_opt, 0);
139
0
  krb5_get_init_creds_opt_set_proxiable(gic_opt, 0);
140
141
0
  if (krb_settings)
142
0
  {
143
0
    if (krb_settings->startTime)
144
0
      start_time = krb_settings->startTime;
145
0
    if (krb_settings->lifeTime)
146
0
      krb5_get_init_creds_opt_set_tkt_life(gic_opt, krb_settings->lifeTime);
147
0
    if (krb_settings->renewLifeTime)
148
0
      krb5_get_init_creds_opt_set_renew_life(gic_opt, krb_settings->renewLifeTime);
149
0
    if (krb_settings->withPac)
150
0
    {
151
0
      rv = krb5_get_init_creds_opt_set_pac_request(ctx, gic_opt, TRUE);
152
0
      if (rv)
153
0
        goto cleanup;
154
0
    }
155
0
    if (krb_settings->armorCache)
156
0
    {
157
0
      rv = krb5_get_init_creds_opt_set_fast_ccache_name(ctx, gic_opt,
158
0
                                                        krb_settings->armorCache);
159
0
      if (rv)
160
0
        goto cleanup;
161
0
    }
162
0
    if (krb_settings->pkinitX509Identity)
163
0
    {
164
0
      rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_user_identity",
165
0
                                          krb_settings->pkinitX509Identity);
166
0
      if (rv)
167
0
        goto cleanup;
168
0
    }
169
0
    if (krb_settings->pkinitX509Anchors)
170
0
    {
171
0
      rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_anchors",
172
0
                                          krb_settings->pkinitX509Anchors);
173
0
      if (rv)
174
0
        goto cleanup;
175
0
    }
176
0
    if (krb_settings->kdcUrl && (strnlen(krb_settings->kdcUrl, 2) > 0))
177
0
    {
178
0
      const char* names[4] = { 0 };
179
0
      char* realm = NULL;
180
0
      char* kdc_url = NULL;
181
0
      size_t size = 0;
182
183
0
      if ((rv = krb5_get_profile(ctx, &profile)))
184
0
        goto cleanup;
185
186
0
      rv = ENOMEM;
187
0
      if (winpr_asprintf(&kdc_url, &size, "https://%s/KdcProxy", krb_settings->kdcUrl) <= 0)
188
0
      {
189
0
        free(kdc_url);
190
0
        goto cleanup;
191
0
      }
192
193
0
      realm = calloc(princ->realm.length + 1, 1);
194
0
      if (!realm)
195
0
      {
196
0
        free(kdc_url);
197
0
        goto cleanup;
198
0
      }
199
0
      CopyMemory(realm, princ->realm.data, princ->realm.length);
200
201
0
      names[0] = "realms";
202
0
      names[1] = realm;
203
0
      names[2] = "kdc";
204
205
0
      profile_clear_relation(profile, names);
206
0
      profile_add_relation(profile, names, kdc_url);
207
208
      /* Since we know who the KDC is, tell krb5 that its certificate is valid for pkinit */
209
0
      names[2] = "pkinit_kdc_hostname";
210
0
      profile_add_relation(profile, names, krb_settings->kdcUrl);
211
212
0
      free(kdc_url);
213
0
      free(realm);
214
215
0
      if ((rv = profile_flush_to_file(profile, tmp_profile_path)))
216
0
        goto cleanup;
217
218
0
      profile_abandon(profile);
219
0
      profile = NULL;
220
0
      if ((rv = profile_init_path(tmp_profile_path, &profile)))
221
0
        goto cleanup;
222
223
0
      if ((rv = krb5_init_context_profile(profile, 0, &ctx)))
224
0
        goto cleanup;
225
0
      is_temp_ctx = TRUE;
226
0
    }
227
0
  }
228
229
0
  if ((rv = krb5_get_init_creds_opt_set_in_ccache(ctx, gic_opt, ccache)))
230
0
    goto cleanup;
231
232
0
  if ((rv = krb5_get_init_creds_opt_set_out_ccache(ctx, gic_opt, ccache)))
233
0
    goto cleanup;
234
235
0
  if ((rv =
236
0
           krb5_init_creds_init(ctx, princ, prompter, password, start_time, gic_opt, &creds_ctx)))
237
0
    goto cleanup;
238
239
0
  if ((rv = krb5_init_creds_get(ctx, creds_ctx)))
240
0
    goto cleanup;
241
242
0
cleanup:
243
0
  krb5_init_creds_free(ctx, creds_ctx);
244
0
  krb5_get_init_creds_opt_free(ctx, gic_opt);
245
0
  if (is_temp_ctx)
246
0
    krb5_free_context(ctx);
247
0
  profile_abandon(profile);
248
0
  winpr_DeleteFile(tmp_profile_path);
249
0
  free(tmp_profile_path);
250
251
0
  return rv;
252
0
}
253