Coverage Report

Created: 2024-05-20 06:11

/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 <winpr/path.h>
25
#include <winpr/wlog.h>
26
#include <winpr/endian.h>
27
#include <winpr/crypto.h>
28
#include <winpr/print.h>
29
#include <winpr/assert.h>
30
#include <errno.h>
31
#include "krb5glue.h"
32
#include <profile.h>
33
34
static char* create_temporary_file(void)
35
0
{
36
0
  BYTE buffer[32];
37
0
  char* hex = NULL;
38
0
  char* path = NULL;
39
40
0
  winpr_RAND(buffer, sizeof(buffer));
41
0
  hex = winpr_BinToHexString(buffer, sizeof(buffer), FALSE);
42
0
  path = GetKnownSubPath(KNOWN_PATH_TEMP, hex);
43
0
  free(hex);
44
0
  return path;
45
0
}
46
47
void krb5glue_keys_free(krb5_context ctx, struct krb5glue_keyset* keyset)
48
0
{
49
0
  WINPR_ASSERT(ctx);
50
0
  WINPR_ASSERT(keyset);
51
52
0
  krb5_k_free_key(ctx, keyset->session_key);
53
0
  krb5_k_free_key(ctx, keyset->initiator_key);
54
0
  krb5_k_free_key(ctx, keyset->acceptor_key);
55
0
}
56
57
krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_ctx, BOOL acceptor,
58
                                       struct krb5glue_keyset* keyset)
59
0
{
60
0
  WINPR_ASSERT(ctx);
61
0
  WINPR_ASSERT(auth_ctx);
62
0
  WINPR_ASSERT(keyset);
63
64
0
  krb5glue_keys_free(ctx, keyset);
65
0
  krb5_auth_con_getkey_k(ctx, auth_ctx, &keyset->session_key);
66
0
  if (acceptor)
67
0
  {
68
0
    krb5_auth_con_getsendsubkey_k(ctx, auth_ctx, &keyset->acceptor_key);
69
0
    krb5_auth_con_getrecvsubkey_k(ctx, auth_ctx, &keyset->initiator_key);
70
0
  }
71
0
  else
72
0
  {
73
0
    krb5_auth_con_getsendsubkey_k(ctx, auth_ctx, &keyset->initiator_key);
74
0
    krb5_auth_con_getrecvsubkey_k(ctx, auth_ctx, &keyset->acceptor_key);
75
0
  }
76
0
  return 0;
77
0
}
78
79
krb5_prompt_type krb5glue_get_prompt_type(krb5_context ctx, krb5_prompt prompts[], int index)
80
0
{
81
0
  WINPR_ASSERT(ctx);
82
0
  WINPR_ASSERT(prompts);
83
84
0
  krb5_prompt_type* types = krb5_get_prompt_types(ctx);
85
0
  return types ? types[index] : 0;
86
0
}
87
88
krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag)
89
0
{
90
0
  krb5_error* error = NULL;
91
0
  krb5_error_code rv = 0;
92
93
0
  WINPR_ASSERT(ctx);
94
0
  WINPR_ASSERT(msg);
95
0
  WINPR_ASSERT(tag);
96
97
0
  if (!(rv = krb5_rd_error(ctx, msg, &error)))
98
0
  {
99
0
    WLog_ERR(tag, "KRB_ERROR: %s", error->text.data);
100
0
    krb5_free_error(ctx, error);
101
0
  }
102
103
0
  return rv;
104
0
}
105
106
BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype,
107
                                            uint32_t* flags)
108
0
{
109
0
  WINPR_ASSERT(flags);
110
111
0
  if (!authenticator || !authenticator->checksum ||
112
0
      authenticator->checksum->checksum_type != cksumtype || authenticator->checksum->length < 24)
113
0
    return FALSE;
114
0
  Data_Read_UINT32((authenticator->checksum->contents + 20), (*flags));
115
0
  return TRUE;
116
0
}
117
118
krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache,
119
                                        krb5_prompter_fct prompter, char* password,
120
                                        SEC_WINPR_KERBEROS_SETTINGS* krb_settings)
121
0
{
122
0
  krb5_error_code rv = 0;
123
0
  krb5_deltat start_time = 0;
124
0
  krb5_get_init_creds_opt* gic_opt = NULL;
125
0
  krb5_init_creds_context creds_ctx = NULL;
126
0
  char* tmp_profile_path = create_temporary_file();
127
0
  profile_t profile = NULL;
128
0
  BOOL is_temp_ctx = FALSE;
129
130
0
  WINPR_ASSERT(ctx);
131
132
0
  rv = krb5_get_init_creds_opt_alloc(ctx, &gic_opt);
133
0
  if (rv)
134
0
    goto cleanup;
135
136
0
  krb5_get_init_creds_opt_set_forwardable(gic_opt, 0);
137
0
  krb5_get_init_creds_opt_set_proxiable(gic_opt, 0);
138
139
0
  if (krb_settings)
140
0
  {
141
0
    if (krb_settings->startTime)
142
0
      start_time = krb_settings->startTime;
143
0
    if (krb_settings->lifeTime)
144
0
      krb5_get_init_creds_opt_set_tkt_life(gic_opt, krb_settings->lifeTime);
145
0
    if (krb_settings->renewLifeTime)
146
0
      krb5_get_init_creds_opt_set_renew_life(gic_opt, krb_settings->renewLifeTime);
147
0
    if (krb_settings->withPac)
148
0
    {
149
0
      rv = krb5_get_init_creds_opt_set_pac_request(ctx, gic_opt, TRUE);
150
0
      if (rv)
151
0
        goto cleanup;
152
0
    }
153
0
    if (krb_settings->armorCache)
154
0
    {
155
0
      rv = krb5_get_init_creds_opt_set_fast_ccache_name(ctx, gic_opt,
156
0
                                                        krb_settings->armorCache);
157
0
      if (rv)
158
0
        goto cleanup;
159
0
    }
160
0
    if (krb_settings->pkinitX509Identity)
161
0
    {
162
0
      rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_user_identity",
163
0
                                          krb_settings->pkinitX509Identity);
164
0
      if (rv)
165
0
        goto cleanup;
166
0
    }
167
0
    if (krb_settings->pkinitX509Anchors)
168
0
    {
169
0
      rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_anchors",
170
0
                                          krb_settings->pkinitX509Anchors);
171
0
      if (rv)
172
0
        goto cleanup;
173
0
    }
174
0
    if (krb_settings->kdcUrl)
175
0
    {
176
0
      const char* names[4] = { 0 };
177
0
      char* realm = NULL;
178
0
      char* kdc_url = NULL;
179
0
      size_t size = 0;
180
181
0
      if ((rv = krb5_get_profile(ctx, &profile)))
182
0
        goto cleanup;
183
184
0
      rv = ENOMEM;
185
0
      if (winpr_asprintf(&kdc_url, &size, "https://%s/KdcProxy", krb_settings->kdcUrl) <= 0)
186
0
      {
187
0
        free(kdc_url);
188
0
        goto cleanup;
189
0
      }
190
191
0
      realm = calloc(princ->realm.length + 1, 1);
192
0
      if (!realm)
193
0
      {
194
0
        free(kdc_url);
195
0
        goto cleanup;
196
0
      }
197
0
      CopyMemory(realm, princ->realm.data, princ->realm.length);
198
199
0
      names[0] = "realms";
200
0
      names[1] = realm;
201
0
      names[2] = "kdc";
202
203
0
      profile_clear_relation(profile, names);
204
0
      profile_add_relation(profile, names, kdc_url);
205
206
      /* Since we know who the KDC is, tell krb5 that its certificate is valid for pkinit */
207
0
      names[2] = "pkinit_kdc_hostname";
208
0
      profile_add_relation(profile, names, krb_settings->kdcUrl);
209
210
0
      free(kdc_url);
211
0
      free(realm);
212
213
0
      if ((rv = profile_flush_to_file(profile, tmp_profile_path)))
214
0
        goto cleanup;
215
216
0
      profile_abandon(profile);
217
0
      profile = NULL;
218
0
      if ((rv = profile_init_path(tmp_profile_path, &profile)))
219
0
        goto cleanup;
220
221
0
      if ((rv = krb5_init_context_profile(profile, 0, &ctx)))
222
0
        goto cleanup;
223
0
      is_temp_ctx = TRUE;
224
0
    }
225
0
  }
226
227
0
  if ((rv = krb5_get_init_creds_opt_set_in_ccache(ctx, gic_opt, ccache)))
228
0
    goto cleanup;
229
230
0
  if ((rv = krb5_get_init_creds_opt_set_out_ccache(ctx, gic_opt, ccache)))
231
0
    goto cleanup;
232
233
0
  if ((rv =
234
0
           krb5_init_creds_init(ctx, princ, prompter, password, start_time, gic_opt, &creds_ctx)))
235
0
    goto cleanup;
236
237
0
  if ((rv = krb5_init_creds_get(ctx, creds_ctx)))
238
0
    goto cleanup;
239
240
0
cleanup:
241
0
  krb5_init_creds_free(ctx, creds_ctx);
242
0
  krb5_get_init_creds_opt_free(ctx, gic_opt);
243
0
  if (is_temp_ctx)
244
0
    krb5_free_context(ctx);
245
0
  profile_abandon(profile);
246
0
  winpr_DeleteFile(tmp_profile_path);
247
0
  free(tmp_profile_path);
248
249
0
  return rv;
250
0
}
251