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