/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 | | |