FreeRDP
kerberos.c
1 
22 #include <winpr/config.h>
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <ctype.h>
30 
31 #include <winpr/assert.h>
32 #include <winpr/asn1.h>
33 #include <winpr/crt.h>
34 #include <winpr/interlocked.h>
35 #include <winpr/sspi.h>
36 #include <winpr/print.h>
37 #include <winpr/tchar.h>
38 #include <winpr/sysinfo.h>
39 #include <winpr/registry.h>
40 #include <winpr/endian.h>
41 #include <winpr/crypto.h>
42 #include <winpr/path.h>
43 #include <winpr/wtypes.h>
44 #include <winpr/winsock.h>
45 #include <winpr/schannel.h>
46 #include <winpr/secapi.h>
47 
48 #include "kerberos.h"
49 
50 #ifdef WITH_KRB5_MIT
51 #include "krb5glue.h"
52 #include <profile.h>
53 #endif
54 
55 #ifdef WITH_KRB5_HEIMDAL
56 #include "krb5glue.h"
57 #include <krb5-protos.h>
58 #endif
59 
60 #include "../sspi.h"
61 #include "../../log.h"
62 #define TAG WINPR_TAG("sspi.Kerberos")
63 
64 #define KRB_TGT_REQ 16
65 #define KRB_TGT_REP 17
66 
67 const SecPkgInfoA KERBEROS_SecPkgInfoA = {
68  0x000F3BBF, /* fCapabilities */
69  1, /* wVersion */
70  0x0010, /* wRPCID */
71  0x0000BB80, /* cbMaxToken : 48k bytes maximum for Windows Server 2012 */
72  "Kerberos", /* Name */
73  "Kerberos Security Package" /* Comment */
74 };
75 
76 static WCHAR KERBEROS_SecPkgInfoW_NameBuffer[32] = { 0 };
77 static WCHAR KERBEROS_SecPkgInfoW_CommentBuffer[32] = { 0 };
78 
79 const SecPkgInfoW KERBEROS_SecPkgInfoW = {
80  0x000F3BBF, /* fCapabilities */
81  1, /* wVersion */
82  0x0010, /* wRPCID */
83  0x0000BB80, /* cbMaxToken : 48k bytes maximum for Windows Server 2012 */
84  KERBEROS_SecPkgInfoW_NameBuffer, /* Name */
85  KERBEROS_SecPkgInfoW_CommentBuffer /* Comment */
86 };
87 
88 #ifdef WITH_KRB5
89 
90 enum KERBEROS_STATE
91 {
92  KERBEROS_STATE_INITIAL,
93  KERBEROS_STATE_TGT_REQ,
94  KERBEROS_STATE_TGT_REP,
95  KERBEROS_STATE_AP_REQ,
96  KERBEROS_STATE_AP_REP,
97  KERBEROS_STATE_FINAL
98 };
99 
100 typedef struct KRB_CREDENTIALS_st
101 {
102  volatile LONG refCount;
103  krb5_context ctx;
104  char* kdc_url;
105  krb5_ccache ccache;
106  krb5_keytab keytab;
107  krb5_keytab client_keytab;
108  BOOL own_ccache;
109 } KRB_CREDENTIALS;
110 
111 struct s_KRB_CONTEXT
112 {
113  enum KERBEROS_STATE state;
114  KRB_CREDENTIALS* credentials;
115  krb5_auth_context auth_ctx;
116  BOOL acceptor;
117  uint32_t flags;
118  uint64_t local_seq;
119  uint64_t remote_seq;
120  struct krb5glue_keyset keyset;
121  BOOL u2u;
122  char* targetHost;
123 };
124 
125 static const WinPrAsn1_OID kerberos_OID = { 9, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
126 static const WinPrAsn1_OID kerberos_u2u_OID = { 10,
127  (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03" };
128 
129 #define krb_log_exec(fkt, ctx, ...) \
130  kerberos_log_msg(ctx, fkt(ctx, ##__VA_ARGS__), #fkt, __FILE__, __func__, __LINE__)
131 #define krb_log_exec_ptr(fkt, ctx, ...) \
132  kerberos_log_msg(*ctx, fkt(ctx, ##__VA_ARGS__), #fkt, __FILE__, __func__, __LINE__)
133 static krb5_error_code kerberos_log_msg(krb5_context ctx, krb5_error_code code, const char* what,
134  const char* file, const char* fkt, size_t line)
135 {
136  switch (code)
137  {
138  case 0:
139  case KRB5_KT_END:
140  break;
141  default:
142  {
143  const DWORD level = WLOG_ERROR;
144 
145  wLog* log = WLog_Get(TAG);
146  if (WLog_IsLevelActive(log, level))
147  {
148  const char* msg = krb5_get_error_message(ctx, code);
149  WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt, "%s (%s [%d])",
150  what, msg, code);
151  krb5_free_error_message(ctx, msg);
152  }
153  }
154  break;
155  }
156  return code;
157 }
158 
159 static void credentials_unref(KRB_CREDENTIALS* credentials);
160 
161 static void kerberos_ContextFree(KRB_CONTEXT* ctx, BOOL allocated)
162 {
163  if (!ctx)
164  return;
165 
166  free(ctx->targetHost);
167  ctx->targetHost = NULL;
168 
169  if (ctx->credentials)
170  {
171  krb5_context krbctx = ctx->credentials->ctx;
172  if (krbctx)
173  {
174  if (ctx->auth_ctx)
175  krb5_auth_con_free(krbctx, ctx->auth_ctx);
176 
177  krb5glue_keys_free(krbctx, &ctx->keyset);
178  }
179 
180  credentials_unref(ctx->credentials);
181  }
182 
183  if (allocated)
184  free(ctx);
185 }
186 
187 static KRB_CONTEXT* kerberos_ContextNew(KRB_CREDENTIALS* credentials)
188 {
189  KRB_CONTEXT* context = NULL;
190 
191  context = (KRB_CONTEXT*)calloc(1, sizeof(KRB_CONTEXT));
192  if (!context)
193  return NULL;
194 
195  context->credentials = credentials;
196  InterlockedIncrement(&credentials->refCount);
197  return context;
198 }
199 
200 static krb5_error_code krb5_prompter(krb5_context context, void* data, const char* name,
201  const char* banner, int num_prompts, krb5_prompt prompts[])
202 {
203  for (int i = 0; i < num_prompts; i++)
204  {
205  krb5_prompt_type type = krb5glue_get_prompt_type(context, prompts, i);
206  if (type && (type == KRB5_PROMPT_TYPE_PREAUTH || type == KRB5_PROMPT_TYPE_PASSWORD) && data)
207  {
208  prompts[i].reply->data = _strdup((const char*)data);
209 
210  const size_t len = strlen((const char*)data);
211  if (len > UINT32_MAX)
212  return KRB5_ERROR;
213  prompts[i].reply->length = (UINT32)len;
214  }
215  }
216  return 0;
217 }
218 
219 static INLINE krb5glue_key get_key(struct krb5glue_keyset* keyset)
220 {
221  return keyset->acceptor_key ? keyset->acceptor_key
222  : keyset->initiator_key ? keyset->initiator_key
223  : keyset->session_key;
224 }
225 
226 static BOOL isValidIPv4(const char* ipAddress)
227 {
228  struct sockaddr_in sa = { 0 };
229  int result = inet_pton(AF_INET, ipAddress, &(sa.sin_addr));
230  return result != 0;
231 }
232 
233 static BOOL isValidIPv6(const char* ipAddress)
234 {
235  struct sockaddr_in6 sa = { 0 };
236  int result = inet_pton(AF_INET6, ipAddress, &(sa.sin6_addr));
237  return result != 0;
238 }
239 
240 static BOOL isValidIP(const char* ipAddress)
241 {
242  return isValidIPv4(ipAddress) || isValidIPv6(ipAddress);
243 }
244 
245 static int build_krbtgt(krb5_context ctx, krb5_data* realm, krb5_principal* ptarget)
246 {
247  /* "krbtgt/" + realm + "@" + realm */
248  size_t len = 0;
249  char* name = NULL;
250  krb5_error_code rv = KRB5_CC_NOMEM;
251 
252  (void)winpr_asprintf(&name, &len, "krbtgt/%s@%s", realm->data, realm->data);
253  if (!name || (len == 0))
254  goto fail;
255 
256  krb5_principal target = { 0 };
257  rv = krb5_parse_name(ctx, name, &target);
258  *ptarget = target;
259 fail:
260  free(name);
261  return rv;
262 }
263 
264 #endif /* WITH_KRB5 */
265 
266 static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleA(
267  SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
268  void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
269  PTimeStamp ptsExpiry)
270 {
271 #ifdef WITH_KRB5
272  SEC_WINPR_KERBEROS_SETTINGS* krb_settings = NULL;
273  KRB_CREDENTIALS* credentials = NULL;
274  krb5_context ctx = NULL;
275  krb5_ccache ccache = NULL;
276  krb5_keytab keytab = NULL;
277  krb5_principal principal = NULL;
278  char* domain = NULL;
279  char* username = NULL;
280  char* password = NULL;
281  BOOL own_ccache = FALSE;
282  const char* const default_ccache_type = "MEMORY";
283 
284  if (pAuthData)
285  {
286  UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
287 
288  if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
289  krb_settings = (((SEC_WINNT_AUTH_IDENTITY_WINPR*)pAuthData)->kerberosSettings);
290 
291  if (!sspi_CopyAuthIdentityFieldsA((const SEC_WINNT_AUTH_IDENTITY_INFO*)pAuthData, &username,
292  &domain, &password))
293  {
294  WLog_ERR(TAG, "Failed to copy auth identity fields");
295  goto cleanup;
296  }
297 
298  if (!pszPrincipal)
299  pszPrincipal = username;
300  }
301 
302  if (krb_log_exec_ptr(krb5_init_context, &ctx))
303  goto cleanup;
304 
305  if (domain)
306  {
307  char* udomain = _strdup(domain);
308  if (!udomain)
309  goto cleanup;
310 
311  CharUpperA(udomain);
312  /* Will use domain if realm is not specified in username */
313  krb5_error_code rv = krb_log_exec(krb5_set_default_realm, ctx, udomain);
314  free(udomain);
315 
316  if (rv)
317  goto cleanup;
318  }
319 
320  if (pszPrincipal)
321  {
322  char* cpszPrincipal = _strdup(pszPrincipal);
323  if (!cpszPrincipal)
324  goto cleanup;
325 
326  /* Find realm component if included and convert to uppercase */
327  char* p = strchr(cpszPrincipal, '@');
328  if (p)
329  CharUpperA(p);
330 
331  krb5_error_code rv = krb_log_exec(krb5_parse_name, ctx, cpszPrincipal, &principal);
332  free(cpszPrincipal);
333 
334  if (rv)
335  goto cleanup;
336  }
337 
338  if (krb_settings && krb_settings->cache)
339  {
340  if ((krb_log_exec(krb5_cc_set_default_name, ctx, krb_settings->cache)))
341  goto cleanup;
342  }
343  else
344  own_ccache = TRUE;
345 
346  if (principal)
347  {
348  /* Use the default cache if it's initialized with the right principal */
349  if (krb5_cc_cache_match(ctx, principal, &ccache) == KRB5_CC_NOTFOUND)
350  {
351  if (own_ccache)
352  {
353  if (krb_log_exec(krb5_cc_new_unique, ctx, default_ccache_type, 0, &ccache))
354  goto cleanup;
355  }
356  else
357  {
358  if (krb_log_exec(krb5_cc_resolve, ctx, krb_settings->cache, &ccache))
359  goto cleanup;
360  }
361 
362  if (krb_log_exec(krb5_cc_initialize, ctx, ccache, principal))
363  goto cleanup;
364  }
365  else
366  own_ccache = FALSE;
367  }
368  else if (fCredentialUse & SECPKG_CRED_OUTBOUND)
369  {
370  /* Use the default cache with it's default principal */
371  if (krb_log_exec(krb5_cc_default, ctx, &ccache))
372  goto cleanup;
373  if (krb_log_exec(krb5_cc_get_principal, ctx, ccache, &principal))
374  goto cleanup;
375  own_ccache = FALSE;
376  }
377  else
378  {
379  if (own_ccache)
380  {
381  if (krb_log_exec(krb5_cc_new_unique, ctx, default_ccache_type, 0, &ccache))
382  goto cleanup;
383  }
384  else
385  {
386  if (krb_log_exec(krb5_cc_resolve, ctx, krb_settings->cache, &ccache))
387  goto cleanup;
388  }
389  }
390 
391  if (krb_settings && krb_settings->keytab)
392  {
393  if (krb_log_exec(krb5_kt_resolve, ctx, krb_settings->keytab, &keytab))
394  goto cleanup;
395  }
396  else
397  {
398  if (fCredentialUse & SECPKG_CRED_INBOUND)
399  if (krb_log_exec(krb5_kt_default, ctx, &keytab))
400  goto cleanup;
401  }
402 
403  /* Get initial credentials if required */
404  if (fCredentialUse & SECPKG_CRED_OUTBOUND)
405  {
406  krb5_creds creds = { 0 };
407  krb5_creds matchCreds = { 0 };
408  int matchFlags = KRB5_TC_MATCH_TIMES;
409 
410  krb5_timeofday(ctx, &matchCreds.times.endtime);
411  matchCreds.times.endtime += 60;
412  matchCreds.client = principal;
413 
414  if (krb_log_exec(build_krbtgt, ctx, &principal->realm, &matchCreds.server))
415  goto cleanup;
416 
417  int rv = krb5_cc_retrieve_cred(ctx, ccache, matchFlags, &matchCreds, &creds);
418  krb5_free_principal(ctx, matchCreds.server);
419  krb5_free_cred_contents(ctx, &creds);
420  if (rv)
421  {
422  if (krb_log_exec(krb5glue_get_init_creds, ctx, principal, ccache, krb5_prompter,
423  password, krb_settings))
424  goto cleanup;
425  }
426  }
427 
428  credentials = calloc(1, sizeof(KRB_CREDENTIALS));
429  if (!credentials)
430  goto cleanup;
431  credentials->refCount = 1;
432  credentials->ctx = ctx;
433  credentials->ccache = ccache;
434  credentials->keytab = keytab;
435  credentials->own_ccache = own_ccache;
436 
437 cleanup:
438 
439  free(domain);
440  free(username);
441  free(password);
442 
443  if (principal)
444  krb5_free_principal(ctx, principal);
445  if (ctx)
446  {
447  if (!credentials)
448  {
449  if (ccache)
450  {
451  if (own_ccache)
452  krb5_cc_destroy(ctx, ccache);
453  else
454  krb5_cc_close(ctx, ccache);
455  }
456  if (keytab)
457  krb5_kt_close(ctx, keytab);
458 
459  krb5_free_context(ctx);
460  }
461  }
462 
463  /* If we managed to get credentials set the output */
464  if (credentials)
465  {
466  sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials);
467  sspi_SecureHandleSetUpperPointer(phCredential, (void*)KERBEROS_SSP_NAME);
468  return SEC_E_OK;
469  }
470 
471  return SEC_E_NO_CREDENTIALS;
472 #else
473  return SEC_E_UNSUPPORTED_FUNCTION;
474 #endif
475 }
476 
477 static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleW(
478  SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
479  void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
480  PTimeStamp ptsExpiry)
481 {
482  SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
483  char* principal = NULL;
484  char* package = NULL;
485 
486  if (pszPrincipal)
487  {
488  principal = ConvertWCharToUtf8Alloc(pszPrincipal, NULL);
489  if (!principal)
490  goto fail;
491  }
492  if (pszPackage)
493  {
494  package = ConvertWCharToUtf8Alloc(pszPackage, NULL);
495  if (!package)
496  goto fail;
497  }
498 
499  status =
500  kerberos_AcquireCredentialsHandleA(principal, package, fCredentialUse, pvLogonID, pAuthData,
501  pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
502 
503 fail:
504  free(principal);
505  free(package);
506 
507  return status;
508 }
509 
510 #ifdef WITH_KRB5
511 static void credentials_unref(KRB_CREDENTIALS* credentials)
512 {
513  WINPR_ASSERT(credentials);
514 
515  if (InterlockedDecrement(&credentials->refCount))
516  return;
517 
518  free(credentials->kdc_url);
519 
520  if (credentials->ccache)
521  {
522  if (credentials->own_ccache)
523  krb5_cc_destroy(credentials->ctx, credentials->ccache);
524  else
525  krb5_cc_close(credentials->ctx, credentials->ccache);
526  }
527  if (credentials->keytab)
528  krb5_kt_close(credentials->ctx, credentials->keytab);
529 
530  krb5_free_context(credentials->ctx);
531  free(credentials);
532 }
533 #endif
534 
535 static SECURITY_STATUS SEC_ENTRY kerberos_FreeCredentialsHandle(PCredHandle phCredential)
536 {
537 #ifdef WITH_KRB5
538  KRB_CREDENTIALS* credentials = sspi_SecureHandleGetLowerPointer(phCredential);
539  if (!credentials)
540  return SEC_E_INVALID_HANDLE;
541 
542  credentials_unref(credentials);
543 
544  sspi_SecureHandleInvalidate(phCredential);
545  return SEC_E_OK;
546 #else
547  return SEC_E_UNSUPPORTED_FUNCTION;
548 #endif
549 }
550 
551 static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesW(PCredHandle phCredential,
552  ULONG ulAttribute,
553  void* pBuffer)
554 {
555 #ifdef WITH_KRB5
556  switch (ulAttribute)
557  {
558  case SECPKG_CRED_ATTR_NAMES:
559  return SEC_E_OK;
560  default:
561  WLog_ERR(TAG, "TODO: QueryCredentialsAttributesW, implement ulAttribute=%08" PRIx32,
562  ulAttribute);
563  return SEC_E_UNSUPPORTED_FUNCTION;
564  }
565 
566 #else
567  return SEC_E_UNSUPPORTED_FUNCTION;
568 #endif
569 }
570 
571 static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesA(PCredHandle phCredential,
572  ULONG ulAttribute,
573  void* pBuffer)
574 {
575  return kerberos_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
576 }
577 
578 #ifdef WITH_KRB5
579 
580 static BOOL kerberos_mk_tgt_token(SecBuffer* buf, int msg_type, char* sname, char* host,
581  const krb5_data* ticket)
582 {
583  WinPrAsn1Encoder* enc = NULL;
585  wStream s;
586  size_t len = 0;
587  sspi_gss_data token;
588  BOOL ret = FALSE;
589 
590  WINPR_ASSERT(buf);
591 
592  if (msg_type != KRB_TGT_REQ && msg_type != KRB_TGT_REP)
593  return FALSE;
594  if (msg_type == KRB_TGT_REP && !ticket)
595  return FALSE;
596 
597  enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
598  if (!enc)
599  return FALSE;
600 
601  /* KERB-TGT-REQUEST (SEQUENCE) */
602  if (!WinPrAsn1EncSeqContainer(enc))
603  goto cleanup;
604 
605  /* pvno [0] INTEGER */
606  if (!WinPrAsn1EncContextualInteger(enc, 0, 5))
607  goto cleanup;
608 
609  /* msg-type [1] INTEGER */
610  if (!WinPrAsn1EncContextualInteger(enc, 1, msg_type))
611  goto cleanup;
612 
613  if (msg_type == KRB_TGT_REQ && sname)
614  {
615  /* server-name [2] PrincipalName (SEQUENCE) */
616  if (!WinPrAsn1EncContextualSeqContainer(enc, 2))
617  goto cleanup;
618 
619  /* name-type [0] INTEGER */
620  if (!WinPrAsn1EncContextualInteger(enc, 0, KRB5_NT_SRV_HST))
621  goto cleanup;
622 
623  /* name-string [1] SEQUENCE OF GeneralString */
624  if (!WinPrAsn1EncContextualSeqContainer(enc, 1))
625  goto cleanup;
626 
627  if (!WinPrAsn1EncGeneralString(enc, sname))
628  goto cleanup;
629 
630  if (host && !WinPrAsn1EncGeneralString(enc, host))
631  goto cleanup;
632 
633  if (!WinPrAsn1EncEndContainer(enc) || !WinPrAsn1EncEndContainer(enc))
634  goto cleanup;
635  }
636  else if (msg_type == KRB_TGT_REP)
637  {
638  /* ticket [2] Ticket */
639  data.data = (BYTE*)ticket->data;
640  data.len = ticket->length;
641  if (!WinPrAsn1EncContextualRawContent(enc, 2, &data))
642  goto cleanup;
643  }
644 
645  if (!WinPrAsn1EncEndContainer(enc))
646  goto cleanup;
647 
648  if (!WinPrAsn1EncStreamSize(enc, &len) || len > buf->cbBuffer)
649  goto cleanup;
650 
651  Stream_StaticInit(&s, buf->pvBuffer, len);
652  if (!WinPrAsn1EncToStream(enc, &s))
653  goto cleanup;
654 
655  token.data = buf->pvBuffer;
656  token.length = (UINT)len;
657  if (sspi_gss_wrap_token(buf, &kerberos_u2u_OID,
658  msg_type == KRB_TGT_REQ ? TOK_ID_TGT_REQ : TOK_ID_TGT_REP, &token))
659  ret = TRUE;
660 
661 cleanup:
662  WinPrAsn1Encoder_Free(&enc);
663  return ret;
664 }
665 
666 static BOOL append(char* dst, size_t dstSize, const char* src)
667 {
668  const size_t dlen = strnlen(dst, dstSize);
669  const size_t slen = strlen(src);
670  if (dlen + slen >= dstSize)
671  return FALSE;
672  if (!strncat(dst, src, slen))
673  return FALSE;
674  return TRUE;
675 }
676 
677 static BOOL kerberos_rd_tgt_req_tag2(WinPrAsn1Decoder* dec, char* buf, size_t len)
678 {
679  BOOL rc = FALSE;
680  WinPrAsn1Decoder seq = { 0 };
681 
682  /* server-name [2] PrincipalName (SEQUENCE) */
683  if (!WinPrAsn1DecReadSequence(dec, &seq))
684  goto end;
685 
686  /* name-type [0] INTEGER */
687  BOOL error = FALSE;
688  WinPrAsn1_INTEGER val = 0;
689  if (!WinPrAsn1DecReadContextualInteger(&seq, 0, &error, &val))
690  goto end;
691 
692  /* name-string [1] SEQUENCE OF GeneralString */
693  if (!WinPrAsn1DecReadContextualSequence(&seq, 1, &error, dec))
694  goto end;
695 
696  WinPrAsn1_tag tag = 0;
697  BOOL first = TRUE;
698  while (WinPrAsn1DecPeekTag(dec, &tag))
699  {
700  BOOL success = FALSE;
701  char* lstr = NULL;
702  if (!WinPrAsn1DecReadGeneralString(dec, &lstr))
703  goto fail;
704 
705  if (!first)
706  {
707  if (!append(buf, len, "/"))
708  goto fail;
709  }
710  first = FALSE;
711 
712  if (!append(buf, len, lstr))
713  goto fail;
714 
715  success = TRUE;
716  fail:
717  free(lstr);
718  if (!success)
719  goto end;
720  }
721 
722  rc = TRUE;
723 end:
724  return rc;
725 }
726 
727 static BOOL kerberos_rd_tgt_req_tag3(WinPrAsn1Decoder* dec, char* buf, size_t len)
728 {
729  /* realm [3] Realm */
730  BOOL rc = FALSE;
731  WinPrAsn1_STRING str = NULL;
732  if (!WinPrAsn1DecReadGeneralString(dec, &str))
733  goto end;
734 
735  if (!append(buf, len, "@"))
736  goto end;
737  if (!append(buf, len, str))
738  goto end;
739 
740  rc = TRUE;
741 end:
742  free(str);
743  return rc;
744 }
745 
746 static BOOL kerberos_rd_tgt_req(WinPrAsn1Decoder* dec, char** target)
747 {
748  BOOL rc = FALSE;
749 
750  if (!target)
751  return FALSE;
752  *target = NULL;
753 
754  wStream s = WinPrAsn1DecGetStream(dec);
755  const size_t len = Stream_Length(&s);
756  if (len == 0)
757  return TRUE;
758 
759  WinPrAsn1Decoder dec2 = { 0 };
760  WinPrAsn1_tagId tag = 0;
761  if (WinPrAsn1DecReadContextualTag(dec, &tag, &dec2) == 0)
762  return FALSE;
763 
764  char* buf = calloc(len + 1, sizeof(char));
765  if (!buf)
766  return FALSE;
767 
768  /* We expect ASN1 context tag values 2 or 3.
769  *
770  * In case we got value 2 an (optional) context tag value 3 might follow.
771  */
772  BOOL checkForTag3 = TRUE;
773  if (tag == 2)
774  {
775  rc = kerberos_rd_tgt_req_tag2(&dec2, buf, len);
776  if (rc)
777  {
778  const size_t res = WinPrAsn1DecReadContextualTag(dec, &tag, dec);
779  if (res == 0)
780  checkForTag3 = FALSE;
781  }
782  }
783 
784  if (checkForTag3)
785  {
786  if (tag == 3)
787  rc = kerberos_rd_tgt_req_tag3(&dec2, buf, len);
788  else
789  rc = FALSE;
790  }
791 
792  if (rc)
793  *target = buf;
794  else
795  free(buf);
796  return rc;
797 }
798 
799 static BOOL kerberos_rd_tgt_rep(WinPrAsn1Decoder* dec, krb5_data* ticket)
800 {
801  if (!ticket)
802  return FALSE;
803 
804  /* ticket [2] Ticket */
805  WinPrAsn1Decoder asnTicket = { 0 };
806  WinPrAsn1_tagId tag = 0;
807  if (WinPrAsn1DecReadContextualTag(dec, &tag, &asnTicket) == 0)
808  return FALSE;
809 
810  if (tag != 2)
811  return FALSE;
812 
813  wStream s = WinPrAsn1DecGetStream(&asnTicket);
814  ticket->data = Stream_BufferAs(&s, char);
815 
816  const size_t len = Stream_Length(&s);
817  if (len > UINT32_MAX)
818  return FALSE;
819  ticket->length = (UINT32)len;
820  return TRUE;
821 }
822 
823 static BOOL kerberos_rd_tgt_token(const sspi_gss_data* token, char** target, krb5_data* ticket)
824 {
825  BOOL error = 0;
826  WinPrAsn1_INTEGER val = 0;
827 
828  WINPR_ASSERT(token);
829 
830  if (target)
831  *target = NULL;
832 
833  WinPrAsn1Decoder der = { 0 };
834  WinPrAsn1Decoder_InitMem(&der, WINPR_ASN1_DER, (BYTE*)token->data, token->length);
835 
836  /* KERB-TGT-REQUEST (SEQUENCE) */
837  WinPrAsn1Decoder seq = { 0 };
838  if (!WinPrAsn1DecReadSequence(&der, &seq))
839  return FALSE;
840 
841  /* pvno [0] INTEGER */
842  if (!WinPrAsn1DecReadContextualInteger(&seq, 0, &error, &val) || val != 5)
843  return FALSE;
844 
845  /* msg-type [1] INTEGER */
846  if (!WinPrAsn1DecReadContextualInteger(&seq, 1, &error, &val))
847  return FALSE;
848 
849  switch (val)
850  {
851  case KRB_TGT_REQ:
852  return kerberos_rd_tgt_req(&seq, target);
853  case KRB_TGT_REP:
854  return kerberos_rd_tgt_rep(&seq, ticket);
855  default:
856  break;
857  }
858  return FALSE;
859 }
860 
861 #endif /* WITH_KRB5 */
862 
863 static BOOL kerberos_hash_channel_bindings(WINPR_DIGEST_CTX* md5, SEC_CHANNEL_BINDINGS* bindings)
864 {
865  BYTE buf[4];
866 
867  Data_Write_UINT32(buf, bindings->dwInitiatorAddrType);
868  if (!winpr_Digest_Update(md5, buf, 4))
869  return FALSE;
870 
871  Data_Write_UINT32(buf, bindings->cbInitiatorLength);
872  if (!winpr_Digest_Update(md5, buf, 4))
873  return FALSE;
874 
875  if (bindings->cbInitiatorLength &&
876  !winpr_Digest_Update(md5, (BYTE*)bindings + bindings->dwInitiatorOffset,
877  bindings->cbInitiatorLength))
878  return FALSE;
879 
880  Data_Write_UINT32(buf, bindings->dwAcceptorAddrType);
881  if (!winpr_Digest_Update(md5, buf, 4))
882  return FALSE;
883 
884  Data_Write_UINT32(buf, bindings->cbAcceptorLength);
885  if (!winpr_Digest_Update(md5, buf, 4))
886  return FALSE;
887 
888  if (bindings->cbAcceptorLength &&
889  !winpr_Digest_Update(md5, (BYTE*)bindings + bindings->dwAcceptorOffset,
890  bindings->cbAcceptorLength))
891  return FALSE;
892 
893  Data_Write_UINT32(buf, bindings->cbApplicationDataLength);
894  if (!winpr_Digest_Update(md5, buf, 4))
895  return FALSE;
896 
897  if (bindings->cbApplicationDataLength &&
898  !winpr_Digest_Update(md5, (BYTE*)bindings + bindings->dwApplicationDataOffset,
899  bindings->cbApplicationDataLength))
900  return FALSE;
901 
902  return TRUE;
903 }
904 
905 static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextA(
906  PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq,
907  ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
908  PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr, PTimeStamp ptsExpiry)
909 {
910 #ifdef WITH_KRB5
911  PSecBuffer input_buffer = NULL;
912  PSecBuffer output_buffer = NULL;
913  PSecBuffer bindings_buffer = NULL;
914  WINPR_DIGEST_CTX* md5 = NULL;
915  char* target = NULL;
916  char* sname = NULL;
917  char* host = NULL;
918  krb5_data input_token = { 0 };
919  krb5_data output_token = { 0 };
920  SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
921  WinPrAsn1_OID oid = { 0 };
922  uint16_t tok_id = 0;
923  krb5_ap_rep_enc_part* reply = NULL;
924  krb5_flags ap_flags = AP_OPTS_USE_SUBKEY;
925  char cksum_contents[24] = { 0 };
926  krb5_data cksum = { 0 };
927  krb5_creds in_creds = { 0 };
928  krb5_creds* creds = NULL;
929  BOOL isNewContext = FALSE;
930  KRB_CONTEXT* context = NULL;
931  KRB_CREDENTIALS* credentials = sspi_SecureHandleGetLowerPointer(phCredential);
932 
933  /* behave like windows SSPIs that don't want empty context */
934  if (phContext && !phContext->dwLower && !phContext->dwUpper)
935  return SEC_E_INVALID_HANDLE;
936 
937  context = sspi_SecureHandleGetLowerPointer(phContext);
938 
939  if (!credentials)
940  return SEC_E_NO_CREDENTIALS;
941 
942  if (pInput)
943  {
944  input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
945  bindings_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
946  }
947  if (pOutput)
948  output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
949 
950  if (fContextReq & ISC_REQ_MUTUAL_AUTH)
951  ap_flags |= AP_OPTS_MUTUAL_REQUIRED;
952 
953  if (fContextReq & ISC_REQ_USE_SESSION_KEY)
954  ap_flags |= AP_OPTS_USE_SESSION_KEY;
955 
956  /* Split target name into service/hostname components */
957  if (pszTargetName)
958  {
959  target = _strdup(pszTargetName);
960  if (!target)
961  {
962  status = SEC_E_INSUFFICIENT_MEMORY;
963  goto cleanup;
964  }
965  host = strchr(target, '/');
966  if (host)
967  {
968  *host++ = 0;
969  sname = target;
970  }
971  else
972  host = target;
973  if (isValidIP(host))
974  {
975  status = SEC_E_NO_CREDENTIALS;
976  goto cleanup;
977  }
978  }
979 
980  if (!context)
981  {
982  context = kerberos_ContextNew(credentials);
983  if (!context)
984  {
985  status = SEC_E_INSUFFICIENT_MEMORY;
986  goto cleanup;
987  }
988 
989  isNewContext = TRUE;
990 
991  if (host)
992  context->targetHost = _strdup(host);
993  if (!context->targetHost)
994  {
995  status = SEC_E_INSUFFICIENT_MEMORY;
996  goto cleanup;
997  }
998 
999  if (fContextReq & ISC_REQ_USE_SESSION_KEY)
1000  {
1001  context->state = KERBEROS_STATE_TGT_REQ;
1002  context->u2u = TRUE;
1003  }
1004  else
1005  context->state = KERBEROS_STATE_AP_REQ;
1006  }
1007  else
1008  {
1009  if (!input_buffer || !sspi_gss_unwrap_token(input_buffer, &oid, &tok_id, &input_token))
1010  goto bad_token;
1011  if ((context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_u2u_OID)) ||
1012  (!context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_OID)))
1013  goto bad_token;
1014  }
1015 
1016  /* SSPI flags are compatible with GSS flags except INTEG_FLAG */
1017  context->flags |= (fContextReq & 0x1F);
1018  if ((fContextReq & ISC_REQ_INTEGRITY) && !(fContextReq & ISC_REQ_NO_INTEGRITY))
1019  context->flags |= SSPI_GSS_C_INTEG_FLAG;
1020 
1021  switch (context->state)
1022  {
1023  case KERBEROS_STATE_TGT_REQ:
1024 
1025  if (!kerberos_mk_tgt_token(output_buffer, KRB_TGT_REQ, sname, host, NULL))
1026  goto cleanup;
1027 
1028  context->state = KERBEROS_STATE_TGT_REP;
1029  status = SEC_I_CONTINUE_NEEDED;
1030  break;
1031 
1032  case KERBEROS_STATE_TGT_REP:
1033 
1034  if (tok_id != TOK_ID_TGT_REP)
1035  goto bad_token;
1036 
1037  if (!kerberos_rd_tgt_token(&input_token, NULL, &in_creds.second_ticket))
1038  goto bad_token;
1039 
1040  /* Continue to AP-REQ */
1041  /* fallthrough */
1042  WINPR_FALLTHROUGH
1043 
1044  case KERBEROS_STATE_AP_REQ:
1045 
1046  /* Set auth_context options */
1047  if (krb_log_exec(krb5_auth_con_init, credentials->ctx, &context->auth_ctx))
1048  goto cleanup;
1049  if (krb_log_exec(krb5_auth_con_setflags, credentials->ctx, context->auth_ctx,
1050  KRB5_AUTH_CONTEXT_DO_SEQUENCE | KRB5_AUTH_CONTEXT_USE_SUBKEY))
1051  goto cleanup;
1052  if (krb_log_exec(krb5glue_auth_con_set_cksumtype, credentials->ctx, context->auth_ctx,
1053  GSS_CHECKSUM_TYPE))
1054  goto cleanup;
1055 
1056  /* Get a service ticket */
1057  if (krb_log_exec(krb5_sname_to_principal, credentials->ctx, host, sname,
1058  KRB5_NT_SRV_HST, &in_creds.server))
1059  goto cleanup;
1060 
1061  if (krb_log_exec(krb5_cc_get_principal, credentials->ctx, credentials->ccache,
1062  &in_creds.client))
1063  {
1064  status = SEC_E_WRONG_PRINCIPAL;
1065  goto cleanup;
1066  }
1067 
1068  if (krb_log_exec(krb5_get_credentials, credentials->ctx,
1069  context->u2u ? KRB5_GC_USER_USER : 0, credentials->ccache, &in_creds,
1070  &creds))
1071  {
1072  status = SEC_E_NO_CREDENTIALS;
1073  goto cleanup;
1074  }
1075 
1076  /* Write the checksum (delegation not implemented) */
1077  cksum.data = cksum_contents;
1078  cksum.length = sizeof(cksum_contents);
1079  Data_Write_UINT32(cksum_contents, 16);
1080  Data_Write_UINT32((cksum_contents + 20), context->flags);
1081 
1082  if (bindings_buffer)
1083  {
1084  SEC_CHANNEL_BINDINGS* bindings = bindings_buffer->pvBuffer;
1085 
1086  /* Sanity checks */
1087  if (bindings_buffer->cbBuffer < sizeof(SEC_CHANNEL_BINDINGS) ||
1088  (bindings->cbInitiatorLength + bindings->dwInitiatorOffset) >
1089  bindings_buffer->cbBuffer ||
1090  (bindings->cbAcceptorLength + bindings->dwAcceptorOffset) >
1091  bindings_buffer->cbBuffer ||
1092  (bindings->cbApplicationDataLength + bindings->dwApplicationDataOffset) >
1093  bindings_buffer->cbBuffer)
1094  {
1095  status = SEC_E_BAD_BINDINGS;
1096  goto cleanup;
1097  }
1098 
1099  md5 = winpr_Digest_New();
1100  if (!md5)
1101  goto cleanup;
1102 
1103  if (!winpr_Digest_Init(md5, WINPR_MD_MD5))
1104  goto cleanup;
1105 
1106  if (!kerberos_hash_channel_bindings(md5, bindings))
1107  goto cleanup;
1108 
1109  if (!winpr_Digest_Final(md5, (BYTE*)cksum_contents + 4, 16))
1110  goto cleanup;
1111  }
1112 
1113  /* Make the AP_REQ message */
1114  if (krb_log_exec(krb5_mk_req_extended, credentials->ctx, &context->auth_ctx, ap_flags,
1115  &cksum, creds, &output_token))
1116  goto cleanup;
1117 
1118  if (!sspi_gss_wrap_token(output_buffer,
1119  context->u2u ? &kerberos_u2u_OID : &kerberos_OID,
1120  TOK_ID_AP_REQ, &output_token))
1121  goto cleanup;
1122 
1123  if (context->flags & SSPI_GSS_C_SEQUENCE_FLAG)
1124  {
1125  if (krb_log_exec(krb5_auth_con_getlocalseqnumber, credentials->ctx,
1126  context->auth_ctx, (INT32*)&context->local_seq))
1127  goto cleanup;
1128  context->remote_seq ^= context->local_seq;
1129  }
1130 
1131  if (krb_log_exec(krb5glue_update_keyset, credentials->ctx, context->auth_ctx, FALSE,
1132  &context->keyset))
1133  goto cleanup;
1134 
1135  context->state = KERBEROS_STATE_AP_REP;
1136 
1137  if (context->flags & SSPI_GSS_C_MUTUAL_FLAG)
1138  status = SEC_I_CONTINUE_NEEDED;
1139  else
1140  status = SEC_E_OK;
1141  break;
1142 
1143  case KERBEROS_STATE_AP_REP:
1144 
1145  if (tok_id == TOK_ID_AP_REP)
1146  {
1147  if (krb_log_exec(krb5_rd_rep, credentials->ctx, context->auth_ctx, &input_token,
1148  &reply))
1149  goto cleanup;
1150  krb5_free_ap_rep_enc_part(credentials->ctx, reply);
1151  }
1152  else if (tok_id == TOK_ID_ERROR)
1153  {
1154  krb5glue_log_error(credentials->ctx, &input_token, TAG);
1155  goto cleanup;
1156  }
1157  else
1158  goto bad_token;
1159 
1160  if (context->flags & SSPI_GSS_C_SEQUENCE_FLAG)
1161  {
1162  if (krb_log_exec(krb5_auth_con_getremoteseqnumber, credentials->ctx,
1163  context->auth_ctx, (INT32*)&context->remote_seq))
1164  goto cleanup;
1165  }
1166 
1167  if (krb_log_exec(krb5glue_update_keyset, credentials->ctx, context->auth_ctx, FALSE,
1168  &context->keyset))
1169  goto cleanup;
1170 
1171  context->state = KERBEROS_STATE_FINAL;
1172 
1173  if (output_buffer)
1174  output_buffer->cbBuffer = 0;
1175  status = SEC_E_OK;
1176  break;
1177 
1178  case KERBEROS_STATE_FINAL:
1179  default:
1180  WLog_ERR(TAG, "Kerberos in invalid state!");
1181  goto cleanup;
1182  }
1183 
1184 cleanup:
1185 {
1186  /* second_ticket is not allocated */
1187  krb5_data edata = { 0 };
1188  in_creds.second_ticket = edata;
1189  krb5_free_cred_contents(credentials->ctx, &in_creds);
1190 }
1191 
1192  krb5_free_creds(credentials->ctx, creds);
1193  if (output_token.data)
1194  krb5glue_free_data_contents(credentials->ctx, &output_token);
1195 
1196  winpr_Digest_Free(md5);
1197 
1198  free(target);
1199 
1200  if (isNewContext)
1201  {
1202  switch (status)
1203  {
1204  case SEC_E_OK:
1205  case SEC_I_CONTINUE_NEEDED:
1206  sspi_SecureHandleSetLowerPointer(phNewContext, context);
1207  sspi_SecureHandleSetUpperPointer(phNewContext, KERBEROS_SSP_NAME);
1208  break;
1209  default:
1210  kerberos_ContextFree(context, TRUE);
1211  break;
1212  }
1213  }
1214 
1215  return status;
1216 
1217 bad_token:
1218  status = SEC_E_INVALID_TOKEN;
1219  goto cleanup;
1220 #else
1221  return SEC_E_UNSUPPORTED_FUNCTION;
1222 #endif /* WITH_KRB5 */
1223 }
1224 
1225 static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextW(
1226  PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,
1227  ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
1228  PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr, PTimeStamp ptsExpiry)
1229 {
1230  SECURITY_STATUS status = 0;
1231  char* target_name = NULL;
1232 
1233  if (pszTargetName)
1234  {
1235  target_name = ConvertWCharToUtf8Alloc(pszTargetName, NULL);
1236  if (!target_name)
1237  return SEC_E_INSUFFICIENT_MEMORY;
1238  }
1239 
1240  status = kerberos_InitializeSecurityContextA(phCredential, phContext, target_name, fContextReq,
1241  Reserved1, TargetDataRep, pInput, Reserved2,
1242  phNewContext, pOutput, pfContextAttr, ptsExpiry);
1243 
1244  if (target_name)
1245  free(target_name);
1246 
1247  return status;
1248 }
1249 
1250 static SECURITY_STATUS SEC_ENTRY kerberos_AcceptSecurityContext(
1251  PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
1252  ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr,
1253  PTimeStamp ptsExpity)
1254 {
1255 #ifdef WITH_KRB5
1256  BOOL isNewContext = FALSE;
1257  PSecBuffer input_buffer = NULL;
1258  PSecBuffer output_buffer = NULL;
1259  WinPrAsn1_OID oid = { 0 };
1260  uint16_t tok_id = 0;
1261  krb5_data input_token = { 0 };
1262  krb5_data output_token = { 0 };
1263  SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1264  krb5_flags ap_flags = 0;
1265  krb5glue_authenticator authenticator = NULL;
1266  char* target = NULL;
1267  char* sname = NULL;
1268  char* realm = NULL;
1269  krb5_kt_cursor cur = { 0 };
1270  krb5_keytab_entry entry = { 0 };
1271  krb5_principal principal = NULL;
1272  krb5_creds creds = { 0 };
1273 
1274  /* behave like windows SSPIs that don't want empty context */
1275  if (phContext && !phContext->dwLower && !phContext->dwUpper)
1276  return SEC_E_INVALID_HANDLE;
1277 
1278  KRB_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1279  KRB_CREDENTIALS* credentials = sspi_SecureHandleGetLowerPointer(phCredential);
1280 
1281  if (pInput)
1282  input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
1283  if (pOutput)
1284  output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
1285 
1286  if (!input_buffer)
1287  return SEC_E_INVALID_TOKEN;
1288 
1289  if (!sspi_gss_unwrap_token(input_buffer, &oid, &tok_id, &input_token))
1290  return SEC_E_INVALID_TOKEN;
1291 
1292  if (!context)
1293  {
1294  isNewContext = TRUE;
1295  context = kerberos_ContextNew(credentials);
1296  context->acceptor = TRUE;
1297 
1298  if (sspi_gss_oid_compare(&oid, &kerberos_u2u_OID))
1299  {
1300  context->u2u = TRUE;
1301  context->state = KERBEROS_STATE_TGT_REQ;
1302  }
1303  else if (sspi_gss_oid_compare(&oid, &kerberos_OID))
1304  context->state = KERBEROS_STATE_AP_REQ;
1305  else
1306  goto bad_token;
1307  }
1308  else
1309  {
1310  if ((context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_u2u_OID)) ||
1311  (!context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_OID)))
1312  goto bad_token;
1313  }
1314 
1315  if (context->state == KERBEROS_STATE_TGT_REQ && tok_id == TOK_ID_TGT_REQ)
1316  {
1317  if (!kerberos_rd_tgt_token(&input_token, &target, NULL))
1318  goto bad_token;
1319 
1320  if (target)
1321  {
1322  if (*target != 0 && *target != '@')
1323  sname = target;
1324  realm = strchr(target, '@');
1325  if (realm)
1326  realm++;
1327  }
1328 
1329  if (krb_log_exec(krb5_parse_name_flags, credentials->ctx, sname ? sname : "",
1330  KRB5_PRINCIPAL_PARSE_NO_REALM, &principal))
1331  goto cleanup;
1332 
1333  if (realm)
1334  {
1335  if (krb_log_exec(krb5glue_set_principal_realm, credentials->ctx, principal, realm))
1336  goto cleanup;
1337  }
1338 
1339  if (krb_log_exec(krb5_kt_start_seq_get, credentials->ctx, credentials->keytab, &cur))
1340  goto cleanup;
1341 
1342  do
1343  {
1344  krb5_error_code rv = krb_log_exec(krb5_kt_next_entry, credentials->ctx,
1345  credentials->keytab, &entry, &cur);
1346  if (rv == KRB5_KT_END)
1347  break;
1348  if (rv != 0)
1349  goto cleanup;
1350 
1351  if ((!sname || krb_log_exec(krb5_principal_compare_any_realm, credentials->ctx,
1352  principal, entry.principal)) &&
1353  (!realm ||
1354  krb_log_exec(krb5_realm_compare, credentials->ctx, principal, entry.principal)))
1355  break;
1356  if (krb_log_exec(krb5glue_free_keytab_entry_contents, credentials->ctx, &entry))
1357  goto cleanup;
1358  } while (1);
1359 
1360  if (krb_log_exec(krb5_kt_end_seq_get, credentials->ctx, credentials->keytab, &cur))
1361  goto cleanup;
1362 
1363  if (!entry.principal)
1364  goto cleanup;
1365 
1366  /* Get the TGT */
1367  if (krb_log_exec(krb5_get_init_creds_keytab, credentials->ctx, &creds, entry.principal,
1368  credentials->keytab, 0, NULL, NULL))
1369  goto cleanup;
1370 
1371  if (!kerberos_mk_tgt_token(output_buffer, KRB_TGT_REP, NULL, NULL, &creds.ticket))
1372  goto cleanup;
1373 
1374  if (krb_log_exec(krb5_auth_con_init, credentials->ctx, &context->auth_ctx))
1375  goto cleanup;
1376 
1377  if (krb_log_exec(krb5glue_auth_con_setuseruserkey, credentials->ctx, context->auth_ctx,
1378  &krb5glue_creds_getkey(creds)))
1379  goto cleanup;
1380 
1381  context->state = KERBEROS_STATE_AP_REQ;
1382  }
1383  else if (context->state == KERBEROS_STATE_AP_REQ && tok_id == TOK_ID_AP_REQ)
1384  {
1385  if (krb_log_exec(krb5_rd_req, credentials->ctx, &context->auth_ctx, &input_token, NULL,
1386  credentials->keytab, &ap_flags, NULL))
1387  goto cleanup;
1388 
1389  if (krb_log_exec(krb5_auth_con_setflags, credentials->ctx, context->auth_ctx,
1390  KRB5_AUTH_CONTEXT_DO_SEQUENCE | KRB5_AUTH_CONTEXT_USE_SUBKEY))
1391  goto cleanup;
1392 
1393  /* Retrieve and validate the checksum */
1394  if (krb_log_exec(krb5_auth_con_getauthenticator, credentials->ctx, context->auth_ctx,
1395  &authenticator))
1396  goto cleanup;
1397  if (!krb5glue_authenticator_validate_chksum(authenticator, GSS_CHECKSUM_TYPE,
1398  &context->flags))
1399  goto bad_token;
1400 
1401  if ((ap_flags & AP_OPTS_MUTUAL_REQUIRED) && (context->flags & SSPI_GSS_C_MUTUAL_FLAG))
1402  {
1403  if (!output_buffer)
1404  goto bad_token;
1405  if (krb_log_exec(krb5_mk_rep, credentials->ctx, context->auth_ctx, &output_token))
1406  goto cleanup;
1407  if (!sspi_gss_wrap_token(output_buffer,
1408  context->u2u ? &kerberos_u2u_OID : &kerberos_OID,
1409  TOK_ID_AP_REP, &output_token))
1410  goto cleanup;
1411  }
1412  else
1413  {
1414  if (output_buffer)
1415  output_buffer->cbBuffer = 0;
1416  }
1417 
1418  *pfContextAttr = (context->flags & 0x1F);
1419  if (context->flags & SSPI_GSS_C_INTEG_FLAG)
1420  *pfContextAttr |= ASC_RET_INTEGRITY;
1421 
1422  if (context->flags & SSPI_GSS_C_SEQUENCE_FLAG)
1423  {
1424  if (krb_log_exec(krb5_auth_con_getlocalseqnumber, credentials->ctx, context->auth_ctx,
1425  (INT32*)&context->local_seq))
1426  goto cleanup;
1427  if (krb_log_exec(krb5_auth_con_getremoteseqnumber, credentials->ctx, context->auth_ctx,
1428  (INT32*)&context->remote_seq))
1429  goto cleanup;
1430  }
1431 
1432  if (krb_log_exec(krb5glue_update_keyset, credentials->ctx, context->auth_ctx, TRUE,
1433  &context->keyset))
1434  goto cleanup;
1435 
1436  context->state = KERBEROS_STATE_FINAL;
1437  }
1438  else
1439  goto bad_token;
1440 
1441  /* On first call allocate new context */
1442  if (context->state == KERBEROS_STATE_FINAL)
1443  status = SEC_E_OK;
1444  else
1445  status = SEC_I_CONTINUE_NEEDED;
1446 
1447 cleanup:
1448  free(target);
1449  if (output_token.data)
1450  krb5glue_free_data_contents(credentials->ctx, &output_token);
1451  if (entry.principal)
1452  krb5glue_free_keytab_entry_contents(credentials->ctx, &entry);
1453 
1454  if (isNewContext)
1455  {
1456  switch (status)
1457  {
1458  case SEC_E_OK:
1459  case SEC_I_CONTINUE_NEEDED:
1460  sspi_SecureHandleSetLowerPointer(phNewContext, context);
1461  sspi_SecureHandleSetUpperPointer(phNewContext, KERBEROS_SSP_NAME);
1462  break;
1463  default:
1464  kerberos_ContextFree(context, TRUE);
1465  break;
1466  }
1467  }
1468 
1469  return status;
1470 
1471 bad_token:
1472  status = SEC_E_INVALID_TOKEN;
1473  goto cleanup;
1474 #else
1475  return SEC_E_UNSUPPORTED_FUNCTION;
1476 #endif /* WITH_KRB5 */
1477 }
1478 
1479 #ifdef WITH_KRB5
1480 static KRB_CONTEXT* get_context(PCtxtHandle phContext)
1481 {
1482  if (!phContext)
1483  return NULL;
1484 
1485  TCHAR* name = sspi_SecureHandleGetUpperPointer(phContext);
1486  if (!name)
1487  return NULL;
1488 
1489  if (_tcsncmp(KERBEROS_SSP_NAME, name, ARRAYSIZE(KERBEROS_SSP_NAME)) != 0)
1490  return NULL;
1491  return sspi_SecureHandleGetLowerPointer(phContext);
1492 }
1493 
1494 static BOOL copy_krb5_data(krb5_data* data, PUCHAR* ptr, ULONG* psize)
1495 {
1496  WINPR_ASSERT(data);
1497  WINPR_ASSERT(ptr);
1498  WINPR_ASSERT(psize);
1499 
1500  *ptr = (PUCHAR)malloc(data->length);
1501  if (!*ptr)
1502  return FALSE;
1503 
1504  *psize = data->length;
1505  memcpy(*ptr, data->data, data->length);
1506  return TRUE;
1507 }
1508 #endif
1509 
1510 static SECURITY_STATUS SEC_ENTRY kerberos_DeleteSecurityContext(PCtxtHandle phContext)
1511 {
1512 #ifdef WITH_KRB5
1513  KRB_CONTEXT* context = get_context(phContext);
1514  if (!context)
1515  return SEC_E_INVALID_HANDLE;
1516 
1517  kerberos_ContextFree(context, TRUE);
1518 
1519  return SEC_E_OK;
1520 #else
1521  return SEC_E_UNSUPPORTED_FUNCTION;
1522 #endif
1523 }
1524 
1525 #ifdef WITH_KRB5
1526 
1527 static SECURITY_STATUS krb5_error_to_SECURITY_STATUS(krb5_error_code code)
1528 {
1529  switch (code)
1530  {
1531  case 0:
1532  return SEC_E_OK;
1533  default:
1534  return SEC_E_INTERNAL_ERROR;
1535  }
1536 }
1537 
1538 static SECURITY_STATUS kerberos_ATTR_SIZES(KRB_CONTEXT* context, KRB_CREDENTIALS* credentials,
1539  SecPkgContext_Sizes* ContextSizes)
1540 {
1541  UINT header = 0;
1542  UINT pad = 0;
1543  UINT trailer = 0;
1544  krb5glue_key key = NULL;
1545 
1546  WINPR_ASSERT(context);
1547  WINPR_ASSERT(context->auth_ctx);
1548 
1549  /* The MaxTokenSize by default is 12,000 bytes. This has been the default value
1550  * since Windows 2000 SP2 and still remains in Windows 7 and Windows 2008 R2.
1551  * For Windows Server 2012, the default value of the MaxTokenSize registry
1552  * entry is 48,000 bytes.*/
1553  ContextSizes->cbMaxToken = KERBEROS_SecPkgInfoA.cbMaxToken;
1554  ContextSizes->cbMaxSignature = 0;
1555  ContextSizes->cbBlockSize = 1;
1556  ContextSizes->cbSecurityTrailer = 0;
1557 
1558  key = get_key(&context->keyset);
1559 
1560  if (context->flags & SSPI_GSS_C_CONF_FLAG)
1561  {
1562  krb5_error_code rv = krb_log_exec(krb5glue_crypto_length, credentials->ctx, key,
1563  KRB5_CRYPTO_TYPE_HEADER, &header);
1564  if (rv)
1565  return krb5_error_to_SECURITY_STATUS(rv);
1566 
1567  rv = krb_log_exec(krb5glue_crypto_length, credentials->ctx, key, KRB5_CRYPTO_TYPE_PADDING,
1568  &pad);
1569  if (rv)
1570  return krb5_error_to_SECURITY_STATUS(rv);
1571 
1572  rv = krb_log_exec(krb5glue_crypto_length, credentials->ctx, key, KRB5_CRYPTO_TYPE_TRAILER,
1573  &trailer);
1574  if (rv)
1575  return krb5_error_to_SECURITY_STATUS(rv);
1576 
1577  /* GSS header (= 16 bytes) + encrypted header = 32 bytes */
1578  ContextSizes->cbSecurityTrailer = header + pad + trailer + 32;
1579  }
1580 
1581  if (context->flags & SSPI_GSS_C_INTEG_FLAG)
1582  {
1583  krb5_error_code rv = krb_log_exec(krb5glue_crypto_length, credentials->ctx, key,
1584  KRB5_CRYPTO_TYPE_CHECKSUM, &ContextSizes->cbMaxSignature);
1585  if (rv)
1586  return krb5_error_to_SECURITY_STATUS(rv);
1587 
1588  ContextSizes->cbMaxSignature += 16;
1589  }
1590 
1591  return SEC_E_OK;
1592 }
1593 
1594 static SECURITY_STATUS kerberos_ATTR_TICKET_LOGON(KRB_CONTEXT* context,
1595  KRB_CREDENTIALS* credentials,
1596  KERB_TICKET_LOGON* ticketLogon)
1597 {
1598  krb5_creds matchCred = { 0 };
1599  krb5_auth_context authContext = NULL;
1600  int getCredsFlags = KRB5_GC_CACHED;
1601  BOOL firstRun = TRUE;
1602  krb5_creds* hostCred = NULL;
1603  SECURITY_STATUS ret = SEC_E_INSUFFICIENT_MEMORY;
1604  int rv = krb_log_exec(krb5_sname_to_principal, credentials->ctx, context->targetHost, "HOST",
1605  KRB5_NT_SRV_HST, &matchCred.server);
1606  if (rv)
1607  goto out;
1608 
1609  rv = krb_log_exec(krb5_cc_get_principal, credentials->ctx, credentials->ccache,
1610  &matchCred.client);
1611  if (rv)
1612  goto out;
1613 
1614  /* try from the cache first, and then do a new request */
1615 again:
1616  rv = krb_log_exec(krb5_get_credentials, credentials->ctx, getCredsFlags, credentials->ccache,
1617  &matchCred, &hostCred);
1618  switch (rv)
1619  {
1620  case 0:
1621  break;
1622  case KRB5_CC_NOTFOUND:
1623  getCredsFlags = 0;
1624  if (firstRun)
1625  {
1626  firstRun = FALSE;
1627  goto again;
1628  }
1629  WINPR_FALLTHROUGH
1630  default:
1631  WLog_ERR(TAG, "krb5_get_credentials(hostCreds), rv=%d", rv);
1632  goto out;
1633  }
1634 
1635  if (krb_log_exec(krb5_auth_con_init, credentials->ctx, &authContext))
1636  goto out;
1637 
1638  krb5_data derOut = { 0 };
1639  if (krb_log_exec(krb5_fwd_tgt_creds, credentials->ctx, authContext, context->targetHost,
1640  matchCred.client, matchCred.server, credentials->ccache, 1, &derOut))
1641  {
1642  ret = SEC_E_LOGON_DENIED;
1643  goto out;
1644  }
1645 
1646  ticketLogon->MessageType = KerbTicketLogon;
1647  ticketLogon->Flags = KERB_LOGON_FLAG_REDIRECTED;
1648 
1649  if (!copy_krb5_data(&hostCred->ticket, &ticketLogon->ServiceTicket,
1650  &ticketLogon->ServiceTicketLength))
1651  {
1652  krb5_free_data(credentials->ctx, &derOut);
1653  goto out;
1654  }
1655 
1656  ticketLogon->TicketGrantingTicketLength = derOut.length;
1657  ticketLogon->TicketGrantingTicket = (PUCHAR)derOut.data;
1658 
1659  ret = SEC_E_OK;
1660 out:
1661  krb5_auth_con_free(credentials->ctx, authContext);
1662  krb5_free_creds(credentials->ctx, hostCred);
1663  krb5_free_cred_contents(credentials->ctx, &matchCred);
1664  return ret;
1665 }
1666 
1667 #endif /* WITH_KRB5 */
1668 
1669 static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesA(PCtxtHandle phContext,
1670  ULONG ulAttribute, void* pBuffer)
1671 {
1672  if (!phContext)
1673  return SEC_E_INVALID_HANDLE;
1674 
1675  if (!pBuffer)
1676  return SEC_E_INVALID_PARAMETER;
1677 
1678 #ifdef WITH_KRB5
1679  KRB_CONTEXT* context = get_context(phContext);
1680  if (!context)
1681  return SEC_E_INVALID_PARAMETER;
1682 
1683  KRB_CREDENTIALS* credentials = context->credentials;
1684 
1685  switch (ulAttribute)
1686  {
1687  case SECPKG_ATTR_SIZES:
1688  return kerberos_ATTR_SIZES(context, credentials, (SecPkgContext_Sizes*)pBuffer);
1689 
1690  case SECPKG_CRED_ATTR_TICKET_LOGON:
1691  return kerberos_ATTR_TICKET_LOGON(context, credentials, (KERB_TICKET_LOGON*)pBuffer);
1692 
1693  default:
1694  WLog_ERR(TAG, "TODO: QueryContextAttributes implement ulAttribute=0x%08" PRIx32,
1695  ulAttribute);
1696  return SEC_E_UNSUPPORTED_FUNCTION;
1697  }
1698 #else
1699  return SEC_E_UNSUPPORTED_FUNCTION;
1700 #endif
1701 }
1702 
1703 static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesW(PCtxtHandle phContext,
1704  ULONG ulAttribute, void* pBuffer)
1705 {
1706  return kerberos_QueryContextAttributesA(phContext, ulAttribute, pBuffer);
1707 }
1708 
1709 static SECURITY_STATUS SEC_ENTRY kerberos_SetContextAttributesW(PCtxtHandle phContext,
1710  ULONG ulAttribute, void* pBuffer,
1711  ULONG cbBuffer)
1712 {
1713  return SEC_E_UNSUPPORTED_FUNCTION;
1714 }
1715 
1716 static SECURITY_STATUS SEC_ENTRY kerberos_SetContextAttributesA(PCtxtHandle phContext,
1717  ULONG ulAttribute, void* pBuffer,
1718  ULONG cbBuffer)
1719 {
1720  return SEC_E_UNSUPPORTED_FUNCTION;
1721 }
1722 
1723 static SECURITY_STATUS SEC_ENTRY kerberos_SetCredentialsAttributesX(PCredHandle phCredential,
1724  ULONG ulAttribute,
1725  void* pBuffer, ULONG cbBuffer,
1726  BOOL unicode)
1727 {
1728 #ifdef WITH_KRB5
1729  KRB_CREDENTIALS* credentials = NULL;
1730 
1731  if (!phCredential)
1732  return SEC_E_INVALID_HANDLE;
1733 
1734  credentials = sspi_SecureHandleGetLowerPointer(phCredential);
1735 
1736  if (!credentials)
1737  return SEC_E_INVALID_HANDLE;
1738 
1739  if (!pBuffer)
1740  return SEC_E_INSUFFICIENT_MEMORY;
1741 
1742  switch (ulAttribute)
1743  {
1744  case SECPKG_CRED_ATTR_KDC_PROXY_SETTINGS:
1745  {
1746  SecPkgCredentials_KdcProxySettingsW* kdc_settings = pBuffer;
1747 
1748  /* Sanity checks */
1749  if (cbBuffer < sizeof(SecPkgCredentials_KdcProxySettingsW) ||
1750  kdc_settings->Version != KDC_PROXY_SETTINGS_V1 ||
1751  kdc_settings->ProxyServerOffset < sizeof(SecPkgCredentials_KdcProxySettingsW) ||
1752  cbBuffer < sizeof(SecPkgCredentials_KdcProxySettingsW) +
1753  kdc_settings->ProxyServerOffset + kdc_settings->ProxyServerLength)
1754  return SEC_E_INVALID_TOKEN;
1755 
1756  if (credentials->kdc_url)
1757  {
1758  free(credentials->kdc_url);
1759  credentials->kdc_url = NULL;
1760  }
1761 
1762  if (kdc_settings->ProxyServerLength > 0)
1763  {
1764  WCHAR* proxy = (WCHAR*)((BYTE*)pBuffer + kdc_settings->ProxyServerOffset);
1765 
1766  credentials->kdc_url = ConvertWCharNToUtf8Alloc(
1767  proxy, kdc_settings->ProxyServerLength / sizeof(WCHAR), NULL);
1768  if (!credentials->kdc_url)
1769  return SEC_E_INSUFFICIENT_MEMORY;
1770  }
1771 
1772  return SEC_E_OK;
1773  }
1774  case SECPKG_CRED_ATTR_NAMES:
1775  case SECPKG_ATTR_SUPPORTED_ALGS:
1776  default:
1777  WLog_ERR(TAG, "TODO: SetCredentialsAttributesX implement ulAttribute=0x%08" PRIx32,
1778  ulAttribute);
1779  return SEC_E_UNSUPPORTED_FUNCTION;
1780  }
1781 
1782 #else
1783  return SEC_E_UNSUPPORTED_FUNCTION;
1784 #endif
1785 }
1786 
1787 static SECURITY_STATUS SEC_ENTRY kerberos_SetCredentialsAttributesW(PCredHandle phCredential,
1788  ULONG ulAttribute,
1789  void* pBuffer, ULONG cbBuffer)
1790 {
1791  return kerberos_SetCredentialsAttributesX(phCredential, ulAttribute, pBuffer, cbBuffer, TRUE);
1792 }
1793 
1794 static SECURITY_STATUS SEC_ENTRY kerberos_SetCredentialsAttributesA(PCredHandle phCredential,
1795  ULONG ulAttribute,
1796  void* pBuffer, ULONG cbBuffer)
1797 {
1798  return kerberos_SetCredentialsAttributesX(phCredential, ulAttribute, pBuffer, cbBuffer, FALSE);
1799 }
1800 
1801 static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
1802  PSecBufferDesc pMessage,
1803  ULONG MessageSeqNo)
1804 {
1805 #ifdef WITH_KRB5
1806  KRB_CONTEXT* context = get_context(phContext);
1807  PSecBuffer sig_buffer = NULL;
1808  PSecBuffer data_buffer = NULL;
1809  char* header = NULL;
1810  BYTE flags = 0;
1811  krb5glue_key key = NULL;
1812  krb5_keyusage usage = 0;
1813  krb5_crypto_iov encrypt_iov[] = { { KRB5_CRYPTO_TYPE_HEADER, { 0 } },
1814  { KRB5_CRYPTO_TYPE_DATA, { 0 } },
1815  { KRB5_CRYPTO_TYPE_DATA, { 0 } },
1816  { KRB5_CRYPTO_TYPE_PADDING, { 0 } },
1817  { KRB5_CRYPTO_TYPE_TRAILER, { 0 } } };
1818 
1819  if (!context)
1820  return SEC_E_INVALID_HANDLE;
1821 
1822  if (!(context->flags & SSPI_GSS_C_CONF_FLAG))
1823  return SEC_E_UNSUPPORTED_FUNCTION;
1824 
1825  KRB_CREDENTIALS* creds = context->credentials;
1826 
1827  sig_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_TOKEN);
1828  data_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA);
1829 
1830  if (!sig_buffer || !data_buffer)
1831  return SEC_E_INVALID_TOKEN;
1832 
1833  if (fQOP)
1834  return SEC_E_QOP_NOT_SUPPORTED;
1835 
1836  flags |= context->acceptor ? FLAG_SENDER_IS_ACCEPTOR : 0;
1837  flags |= FLAG_WRAP_CONFIDENTIAL;
1838 
1839  key = get_key(&context->keyset);
1840  if (!key)
1841  return SEC_E_INTERNAL_ERROR;
1842 
1843  flags |= context->keyset.acceptor_key == key ? FLAG_ACCEPTOR_SUBKEY : 0;
1844 
1845  usage = context->acceptor ? KG_USAGE_ACCEPTOR_SEAL : KG_USAGE_INITIATOR_SEAL;
1846 
1847  /* Set the lengths of the data (plaintext + header) */
1848  encrypt_iov[1].data.length = data_buffer->cbBuffer;
1849  encrypt_iov[2].data.length = 16;
1850 
1851  /* Get the lengths of the header, trailer, and padding and ensure sig_buffer is large enough */
1852  if (krb_log_exec(krb5glue_crypto_length_iov, creds->ctx, key, encrypt_iov,
1853  ARRAYSIZE(encrypt_iov)))
1854  return SEC_E_INTERNAL_ERROR;
1855  if (sig_buffer->cbBuffer <
1856  encrypt_iov[0].data.length + encrypt_iov[3].data.length + encrypt_iov[4].data.length + 32)
1857  return SEC_E_INSUFFICIENT_MEMORY;
1858 
1859  /* Set up the iov array in sig_buffer */
1860  header = sig_buffer->pvBuffer;
1861  encrypt_iov[2].data.data = header + 16;
1862  encrypt_iov[3].data.data = encrypt_iov[2].data.data + encrypt_iov[2].data.length;
1863  encrypt_iov[4].data.data = encrypt_iov[3].data.data + encrypt_iov[3].data.length;
1864  encrypt_iov[0].data.data = encrypt_iov[4].data.data + encrypt_iov[4].data.length;
1865  encrypt_iov[1].data.data = data_buffer->pvBuffer;
1866 
1867  /* Write the GSS header with 0 in RRC */
1868  Data_Write_UINT16_BE(header, TOK_ID_WRAP);
1869  header[2] = flags;
1870  header[3] = (char)0xFF;
1871  Data_Write_UINT32(header + 4, 0);
1872  Data_Write_UINT64_BE(header + 8, (context->local_seq + MessageSeqNo));
1873 
1874  /* Copy header to be encrypted */
1875  CopyMemory(encrypt_iov[2].data.data, header, 16);
1876 
1877  /* Set the correct RRC */
1878  Data_Write_UINT16_BE(header + 6, 16 + encrypt_iov[3].data.length + encrypt_iov[4].data.length);
1879 
1880  if (krb_log_exec(krb5glue_encrypt_iov, creds->ctx, key, usage, encrypt_iov,
1881  ARRAYSIZE(encrypt_iov)))
1882  return SEC_E_INTERNAL_ERROR;
1883 
1884  return SEC_E_OK;
1885 #else
1886  return SEC_E_UNSUPPORTED_FUNCTION;
1887 #endif
1888 }
1889 
1890 static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(PCtxtHandle phContext,
1891  PSecBufferDesc pMessage,
1892  ULONG MessageSeqNo, ULONG* pfQOP)
1893 {
1894 #ifdef WITH_KRB5
1895  KRB_CONTEXT* context = get_context(phContext);
1896  PSecBuffer sig_buffer = NULL;
1897  PSecBuffer data_buffer = NULL;
1898  krb5glue_key key = NULL;
1899  krb5_keyusage usage = 0;
1900  char* header = NULL;
1901  uint16_t tok_id = 0;
1902  BYTE flags = 0;
1903  uint16_t ec = 0;
1904  uint16_t rrc = 0;
1905  uint64_t seq_no = 0;
1906  krb5_crypto_iov iov[] = { { KRB5_CRYPTO_TYPE_HEADER, { 0 } },
1907  { KRB5_CRYPTO_TYPE_DATA, { 0 } },
1908  { KRB5_CRYPTO_TYPE_DATA, { 0 } },
1909  { KRB5_CRYPTO_TYPE_PADDING, { 0 } },
1910  { KRB5_CRYPTO_TYPE_TRAILER, { 0 } } };
1911 
1912  if (!context)
1913  return SEC_E_INVALID_HANDLE;
1914 
1915  if (!(context->flags & SSPI_GSS_C_CONF_FLAG))
1916  return SEC_E_UNSUPPORTED_FUNCTION;
1917 
1918  KRB_CREDENTIALS* creds = context->credentials;
1919 
1920  sig_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_TOKEN);
1921  data_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA);
1922 
1923  if (!sig_buffer || !data_buffer || sig_buffer->cbBuffer < 16)
1924  return SEC_E_INVALID_TOKEN;
1925 
1926  /* Read in header information */
1927  header = sig_buffer->pvBuffer;
1928  Data_Read_UINT16_BE(header, tok_id);
1929  flags = header[2];
1930  Data_Read_UINT16_BE((header + 4), ec);
1931  Data_Read_UINT16_BE((header + 6), rrc);
1932  Data_Read_UINT64_BE((header + 8), seq_no);
1933 
1934  /* Check that the header is valid */
1935  if (tok_id != TOK_ID_WRAP || (BYTE)header[3] != 0xFF)
1936  return SEC_E_INVALID_TOKEN;
1937 
1938  if ((flags & FLAG_SENDER_IS_ACCEPTOR) == context->acceptor)
1939  return SEC_E_INVALID_TOKEN;
1940 
1941  if ((context->flags & ISC_REQ_SEQUENCE_DETECT) &&
1942  (seq_no != context->remote_seq + MessageSeqNo))
1943  return SEC_E_OUT_OF_SEQUENCE;
1944 
1945  if (!(flags & FLAG_WRAP_CONFIDENTIAL))
1946  return SEC_E_INVALID_TOKEN;
1947 
1948  /* We don't expect a trailer buffer; the encrypted header must be rotated */
1949  if (rrc < 16)
1950  return SEC_E_INVALID_TOKEN;
1951 
1952  /* Find the proper key and key usage */
1953  key = get_key(&context->keyset);
1954  if (!key || ((flags & FLAG_ACCEPTOR_SUBKEY) && (context->keyset.acceptor_key != key)))
1955  return SEC_E_INTERNAL_ERROR;
1956  usage = context->acceptor ? KG_USAGE_INITIATOR_SEAL : KG_USAGE_ACCEPTOR_SEAL;
1957 
1958  /* Fill in the lengths of the iov array */
1959  iov[1].data.length = data_buffer->cbBuffer;
1960  iov[2].data.length = 16;
1961  if (krb_log_exec(krb5glue_crypto_length_iov, creds->ctx, key, iov, ARRAYSIZE(iov)))
1962  return SEC_E_INTERNAL_ERROR;
1963 
1964  /* We don't expect a trailer buffer; everything must be in sig_buffer */
1965  if (rrc != 16 + iov[3].data.length + iov[4].data.length)
1966  return SEC_E_INVALID_TOKEN;
1967  if (sig_buffer->cbBuffer != 16 + rrc + iov[0].data.length)
1968  return SEC_E_INVALID_TOKEN;
1969 
1970  /* Locate the parts of the message */
1971  iov[0].data.data = header + 16 + rrc + ec;
1972  iov[1].data.data = data_buffer->pvBuffer;
1973  iov[2].data.data = header + 16 + ec;
1974  iov[3].data.data = iov[2].data.data + iov[2].data.length;
1975  iov[4].data.data = iov[3].data.data + iov[3].data.length;
1976 
1977  if (krb_log_exec(krb5glue_decrypt_iov, creds->ctx, key, usage, iov, ARRAYSIZE(iov)))
1978  return SEC_E_INTERNAL_ERROR;
1979 
1980  /* Validate the encrypted header */
1981  Data_Write_UINT16_BE(iov[2].data.data + 4, ec);
1982  Data_Write_UINT16_BE(iov[2].data.data + 6, rrc);
1983  if (memcmp(iov[2].data.data, header, 16) != 0)
1984  return SEC_E_MESSAGE_ALTERED;
1985 
1986  *pfQOP = 0;
1987 
1988  return SEC_E_OK;
1989 #else
1990  return SEC_E_UNSUPPORTED_FUNCTION;
1991 #endif
1992 }
1993 
1994 static SECURITY_STATUS SEC_ENTRY kerberos_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
1995  PSecBufferDesc pMessage, ULONG MessageSeqNo)
1996 {
1997 #ifdef WITH_KRB5
1998  KRB_CONTEXT* context = get_context(phContext);
1999  PSecBuffer sig_buffer = NULL;
2000  PSecBuffer data_buffer = NULL;
2001  krb5glue_key key = NULL;
2002  krb5_keyusage usage = 0;
2003  char* header = NULL;
2004  BYTE flags = 0;
2005  krb5_crypto_iov iov[] = { { KRB5_CRYPTO_TYPE_DATA, { 0 } },
2006  { KRB5_CRYPTO_TYPE_DATA, { 0 } },
2007  { KRB5_CRYPTO_TYPE_CHECKSUM, { 0 } } };
2008 
2009  if (!context)
2010  return SEC_E_INVALID_HANDLE;
2011 
2012  if (!(context->flags & SSPI_GSS_C_INTEG_FLAG))
2013  return SEC_E_UNSUPPORTED_FUNCTION;
2014 
2015  KRB_CREDENTIALS* creds = context->credentials;
2016 
2017  sig_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_TOKEN);
2018  data_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA);
2019 
2020  if (!sig_buffer || !data_buffer)
2021  return SEC_E_INVALID_TOKEN;
2022 
2023  flags |= context->acceptor ? FLAG_SENDER_IS_ACCEPTOR : 0;
2024 
2025  key = get_key(&context->keyset);
2026  if (!key)
2027  return SEC_E_INTERNAL_ERROR;
2028  usage = context->acceptor ? KG_USAGE_ACCEPTOR_SIGN : KG_USAGE_INITIATOR_SIGN;
2029 
2030  flags |= context->keyset.acceptor_key == key ? FLAG_ACCEPTOR_SUBKEY : 0;
2031 
2032  /* Fill in the lengths of the iov array */
2033  iov[0].data.length = data_buffer->cbBuffer;
2034  iov[1].data.length = 16;
2035  if (krb_log_exec(krb5glue_crypto_length_iov, creds->ctx, key, iov, ARRAYSIZE(iov)))
2036  return SEC_E_INTERNAL_ERROR;
2037 
2038  /* Ensure the buffer is big enough */
2039  if (sig_buffer->cbBuffer < iov[2].data.length + 16)
2040  return SEC_E_INSUFFICIENT_MEMORY;
2041 
2042  /* Write the header */
2043  header = sig_buffer->pvBuffer;
2044  Data_Write_UINT16_BE(header, TOK_ID_MIC);
2045  header[2] = flags;
2046  memset(header + 3, 0xFF, 5);
2047  Data_Write_UINT64_BE(header + 8, (context->local_seq + MessageSeqNo));
2048 
2049  /* Set up the iov array */
2050  iov[0].data.data = data_buffer->pvBuffer;
2051  iov[1].data.data = header;
2052  iov[2].data.data = header + 16;
2053 
2054  if (krb_log_exec(krb5glue_make_checksum_iov, creds->ctx, key, usage, iov, ARRAYSIZE(iov)))
2055  return SEC_E_INTERNAL_ERROR;
2056 
2057  sig_buffer->cbBuffer = iov[2].data.length + 16;
2058 
2059  return SEC_E_OK;
2060 #else
2061  return SEC_E_UNSUPPORTED_FUNCTION;
2062 #endif
2063 }
2064 
2065 static SECURITY_STATUS SEC_ENTRY kerberos_VerifySignature(PCtxtHandle phContext,
2066  PSecBufferDesc pMessage,
2067  ULONG MessageSeqNo, ULONG* pfQOP)
2068 {
2069 #ifdef WITH_KRB5
2070  PSecBuffer sig_buffer = NULL;
2071  PSecBuffer data_buffer = NULL;
2072  krb5glue_key key = NULL;
2073  krb5_keyusage usage = 0;
2074  char* header = NULL;
2075  BYTE flags = 0;
2076  uint16_t tok_id = 0;
2077  uint64_t seq_no = 0;
2078  krb5_boolean is_valid = 0;
2079  krb5_crypto_iov iov[] = { { KRB5_CRYPTO_TYPE_DATA, { 0 } },
2080  { KRB5_CRYPTO_TYPE_DATA, { 0 } },
2081  { KRB5_CRYPTO_TYPE_CHECKSUM, { 0 } } };
2082  BYTE cmp_filler[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
2083 
2084  KRB_CONTEXT* context = get_context(phContext);
2085  if (!context)
2086  return SEC_E_INVALID_HANDLE;
2087 
2088  if (!(context->flags & SSPI_GSS_C_INTEG_FLAG))
2089  return SEC_E_UNSUPPORTED_FUNCTION;
2090 
2091  sig_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_TOKEN);
2092  data_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA);
2093 
2094  if (!sig_buffer || !data_buffer || sig_buffer->cbBuffer < 16)
2095  return SEC_E_INVALID_TOKEN;
2096 
2097  /* Read in header info */
2098  header = sig_buffer->pvBuffer;
2099  Data_Read_UINT16_BE(header, tok_id);
2100  flags = header[2];
2101  Data_Read_UINT64_BE((header + 8), seq_no);
2102 
2103  /* Validate header */
2104  if (tok_id != TOK_ID_MIC)
2105  return SEC_E_INVALID_TOKEN;
2106 
2107  if ((flags & FLAG_SENDER_IS_ACCEPTOR) == context->acceptor || flags & FLAG_WRAP_CONFIDENTIAL)
2108  return SEC_E_INVALID_TOKEN;
2109 
2110  if (memcmp(header + 3, cmp_filler, sizeof(cmp_filler)) != 0)
2111  return SEC_E_INVALID_TOKEN;
2112 
2113  if (context->flags & ISC_REQ_SEQUENCE_DETECT && seq_no != context->remote_seq + MessageSeqNo)
2114  return SEC_E_OUT_OF_SEQUENCE;
2115 
2116  /* Find the proper key and usage */
2117  key = get_key(&context->keyset);
2118  if (!key || (flags & FLAG_ACCEPTOR_SUBKEY && context->keyset.acceptor_key != key))
2119  return SEC_E_INTERNAL_ERROR;
2120  usage = context->acceptor ? KG_USAGE_INITIATOR_SIGN : KG_USAGE_ACCEPTOR_SIGN;
2121 
2122  /* Fill in the iov array lengths */
2123  KRB_CREDENTIALS* creds = context->credentials;
2124  iov[0].data.length = data_buffer->cbBuffer;
2125  iov[1].data.length = 16;
2126  if (krb_log_exec(krb5glue_crypto_length_iov, creds->ctx, key, iov, ARRAYSIZE(iov)))
2127  return SEC_E_INTERNAL_ERROR;
2128 
2129  if (sig_buffer->cbBuffer != iov[2].data.length + 16)
2130  return SEC_E_INTERNAL_ERROR;
2131 
2132  /* Set up the iov array */
2133  iov[0].data.data = data_buffer->pvBuffer;
2134  iov[1].data.data = header;
2135  iov[2].data.data = header + 16;
2136 
2137  if (krb_log_exec(krb5glue_verify_checksum_iov, creds->ctx, key, usage, iov, ARRAYSIZE(iov),
2138  &is_valid))
2139  return SEC_E_INTERNAL_ERROR;
2140 
2141  if (!is_valid)
2142  return SEC_E_MESSAGE_ALTERED;
2143 
2144  return SEC_E_OK;
2145 #else
2146  return SEC_E_UNSUPPORTED_FUNCTION;
2147 #endif
2148 }
2149 
2150 const SecurityFunctionTableA KERBEROS_SecurityFunctionTableA = {
2151  3, /* dwVersion */
2152  NULL, /* EnumerateSecurityPackages */
2153  kerberos_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
2154  kerberos_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
2155  kerberos_FreeCredentialsHandle, /* FreeCredentialsHandle */
2156  NULL, /* Reserved2 */
2157  kerberos_InitializeSecurityContextA, /* InitializeSecurityContext */
2158  kerberos_AcceptSecurityContext, /* AcceptSecurityContext */
2159  NULL, /* CompleteAuthToken */
2160  kerberos_DeleteSecurityContext, /* DeleteSecurityContext */
2161  NULL, /* ApplyControlToken */
2162  kerberos_QueryContextAttributesA, /* QueryContextAttributes */
2163  NULL, /* ImpersonateSecurityContext */
2164  NULL, /* RevertSecurityContext */
2165  kerberos_MakeSignature, /* MakeSignature */
2166  kerberos_VerifySignature, /* VerifySignature */
2167  NULL, /* FreeContextBuffer */
2168  NULL, /* QuerySecurityPackageInfo */
2169  NULL, /* Reserved3 */
2170  NULL, /* Reserved4 */
2171  NULL, /* ExportSecurityContext */
2172  NULL, /* ImportSecurityContext */
2173  NULL, /* AddCredentials */
2174  NULL, /* Reserved8 */
2175  NULL, /* QuerySecurityContextToken */
2176  kerberos_EncryptMessage, /* EncryptMessage */
2177  kerberos_DecryptMessage, /* DecryptMessage */
2178  kerberos_SetContextAttributesA, /* SetContextAttributes */
2179  kerberos_SetCredentialsAttributesA, /* SetCredentialsAttributes */
2180 };
2181 
2182 const SecurityFunctionTableW KERBEROS_SecurityFunctionTableW = {
2183  3, /* dwVersion */
2184  NULL, /* EnumerateSecurityPackages */
2185  kerberos_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
2186  kerberos_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
2187  kerberos_FreeCredentialsHandle, /* FreeCredentialsHandle */
2188  NULL, /* Reserved2 */
2189  kerberos_InitializeSecurityContextW, /* InitializeSecurityContext */
2190  kerberos_AcceptSecurityContext, /* AcceptSecurityContext */
2191  NULL, /* CompleteAuthToken */
2192  kerberos_DeleteSecurityContext, /* DeleteSecurityContext */
2193  NULL, /* ApplyControlToken */
2194  kerberos_QueryContextAttributesW, /* QueryContextAttributes */
2195  NULL, /* ImpersonateSecurityContext */
2196  NULL, /* RevertSecurityContext */
2197  kerberos_MakeSignature, /* MakeSignature */
2198  kerberos_VerifySignature, /* VerifySignature */
2199  NULL, /* FreeContextBuffer */
2200  NULL, /* QuerySecurityPackageInfo */
2201  NULL, /* Reserved3 */
2202  NULL, /* Reserved4 */
2203  NULL, /* ExportSecurityContext */
2204  NULL, /* ImportSecurityContext */
2205  NULL, /* AddCredentials */
2206  NULL, /* Reserved8 */
2207  NULL, /* QuerySecurityContextToken */
2208  kerberos_EncryptMessage, /* EncryptMessage */
2209  kerberos_DecryptMessage, /* DecryptMessage */
2210  kerberos_SetContextAttributesW, /* SetContextAttributes */
2211  kerberos_SetCredentialsAttributesW, /* SetCredentialsAttributes */
2212 };
2213 
2214 BOOL KERBEROS_init(void)
2215 {
2216  InitializeConstWCharFromUtf8(KERBEROS_SecPkgInfoA.Name, KERBEROS_SecPkgInfoW_NameBuffer,
2217  ARRAYSIZE(KERBEROS_SecPkgInfoW_NameBuffer));
2218  InitializeConstWCharFromUtf8(KERBEROS_SecPkgInfoA.Comment, KERBEROS_SecPkgInfoW_CommentBuffer,
2219  ARRAYSIZE(KERBEROS_SecPkgInfoW_CommentBuffer));
2220  return TRUE;
2221 }