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