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