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