2 * Copyright 2017 Dmitry Timoshkov
3 * Copyright 2017 George Popoff
4 * Copyright 2008 Robert Shearman for CodeWeavers
5 * Copyright 2017 Hans Leidekker for CodeWeavers
7 * Kerberos5 Authentication Package
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "wine/port.h"
28 #ifdef HAVE_KRB5_KRB5_H
29 #include <krb5/krb5.h>
31 #ifdef SONAME_LIBGSSAPI_KRB5
32 # include <gssapi/gssapi.h>
33 # include <gssapi/gssapi_ext.h>
37 #define WIN32_NO_STATUS
46 #include "wine/heap.h"
47 #include "wine/library.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(kerberos
);
52 #define KERBEROS_MAX_BUF 12000
54 #define KERBEROS_CAPS \
55 ( SECPKG_FLAG_INTEGRITY \
56 | SECPKG_FLAG_PRIVACY \
57 | SECPKG_FLAG_TOKEN_ONLY \
58 | SECPKG_FLAG_DATAGRAM \
59 | SECPKG_FLAG_CONNECTION \
60 | SECPKG_FLAG_MULTI_REQUIRED \
61 | SECPKG_FLAG_EXTENDED_ERROR \
62 | SECPKG_FLAG_IMPERSONATION \
63 | SECPKG_FLAG_ACCEPT_WIN32_NAME \
64 | SECPKG_FLAG_NEGOTIABLE \
65 | SECPKG_FLAG_GSS_COMPATIBLE \
67 | SECPKG_FLAG_MUTUAL_AUTH \
68 | SECPKG_FLAG_DELEGATION \
69 | SECPKG_FLAG_READONLY_WITH_CHECKSUM \
70 | SECPKG_FLAG_RESTRICTED_TOKENS \
71 | SECPKG_FLAG_APPCONTAINER_CHECKS)
73 static WCHAR kerberos_name_W
[] = {'K','e','r','b','e','r','o','s',0};
74 static WCHAR kerberos_comment_W
[] = {'M','i','c','r','o','s','o','f','t',' ','K','e','r','b','e','r','o','s',' ','V','1','.','0',0};
75 static const SecPkgInfoW infoW
=
79 RPC_C_AUTHN_GSS_KERBEROS
,
85 static ULONG kerberos_package_id
;
86 static LSA_DISPATCH_TABLE lsa_dispatch
;
90 static void *libkrb5_handle
;
92 #define MAKE_FUNCPTR(f) static typeof(f) * p_##f
93 MAKE_FUNCPTR(krb5_init_context
);
94 MAKE_FUNCPTR(krb5_free_context
);
95 MAKE_FUNCPTR(krb5_free_ticket
);
96 MAKE_FUNCPTR(krb5_cccol_cursor_new
);
97 MAKE_FUNCPTR(krb5_cccol_cursor_next
);
98 MAKE_FUNCPTR(krb5_cccol_cursor_free
);
99 MAKE_FUNCPTR(krb5_cc_close
);
100 MAKE_FUNCPTR(krb5_cc_start_seq_get
);
101 MAKE_FUNCPTR(krb5_cc_end_seq_get
);
102 MAKE_FUNCPTR(krb5_cc_next_cred
);
103 MAKE_FUNCPTR(krb5_is_config_principal
);
104 MAKE_FUNCPTR(krb5_decode_ticket
);
105 MAKE_FUNCPTR(krb5_unparse_name_flags
);
106 MAKE_FUNCPTR(krb5_free_unparsed_name
);
107 MAKE_FUNCPTR(krb5_free_cred_contents
);
110 static void load_krb5(void)
112 if (!(libkrb5_handle
= wine_dlopen(SONAME_LIBKRB5
, RTLD_NOW
, NULL
, 0)))
114 WARN("Failed to load %s, Kerberos support will be disabled\n", SONAME_LIBKRB5
);
118 #define LOAD_FUNCPTR(f) \
119 if (!(p_##f = wine_dlsym(libkrb5_handle, #f, NULL, 0))) \
121 ERR("Failed to load %s\n", #f); \
125 LOAD_FUNCPTR(krb5_init_context
)
126 LOAD_FUNCPTR(krb5_free_context
)
127 LOAD_FUNCPTR(krb5_free_ticket
)
128 LOAD_FUNCPTR(krb5_cccol_cursor_new
)
129 LOAD_FUNCPTR(krb5_cccol_cursor_next
)
130 LOAD_FUNCPTR(krb5_cccol_cursor_free
)
131 LOAD_FUNCPTR(krb5_cc_close
)
132 LOAD_FUNCPTR(krb5_cc_start_seq_get
)
133 LOAD_FUNCPTR(krb5_cc_end_seq_get
)
134 LOAD_FUNCPTR(krb5_cc_next_cred
)
135 LOAD_FUNCPTR(krb5_is_config_principal
)
136 LOAD_FUNCPTR(krb5_decode_ticket
)
137 LOAD_FUNCPTR(krb5_unparse_name_flags
)
138 LOAD_FUNCPTR(krb5_free_unparsed_name
)
139 LOAD_FUNCPTR(krb5_free_cred_contents
)
145 wine_dlclose(libkrb5_handle
, NULL
, 0);
146 libkrb5_handle
= NULL
;
149 #else /* SONAME_LIBKRB5 */
151 static void load_krb5(void)
153 WARN("Kerberos support was not provided at compile time\n");
156 #endif /* SONAME_LIBKRB5 */
158 static const char *debugstr_us( const UNICODE_STRING
*us
)
160 if (!us
) return "<null>";
161 return debugstr_wn( us
->Buffer
, us
->Length
/ sizeof(WCHAR
) );
164 static NTSTATUS NTAPI
kerberos_LsaApInitializePackage(ULONG package_id
, PLSA_DISPATCH_TABLE dispatch
,
165 PLSA_STRING database
, PLSA_STRING confidentiality
, PLSA_STRING
*package_name
)
171 kerberos_package_id
= package_id
;
172 lsa_dispatch
= *dispatch
;
174 kerberos_name
= lsa_dispatch
.AllocateLsaHeap(sizeof(MICROSOFT_KERBEROS_NAME_A
));
175 if (!kerberos_name
) return STATUS_NO_MEMORY
;
177 memcpy(kerberos_name
, MICROSOFT_KERBEROS_NAME_A
, sizeof(MICROSOFT_KERBEROS_NAME_A
));
179 *package_name
= lsa_dispatch
.AllocateLsaHeap(sizeof(**package_name
));
182 lsa_dispatch
.FreeLsaHeap(kerberos_name
);
183 return STATUS_NO_MEMORY
;
186 RtlInitString(*package_name
, kerberos_name
);
188 return STATUS_SUCCESS
;
191 #ifdef SONAME_LIBKRB5
195 ULONG count
, allocated
;
196 KERB_TICKET_CACHE_INFO
*info
;
199 static NTSTATUS
krb5_error_to_status(krb5_error_code error
)
203 case 0: return STATUS_SUCCESS
;
206 return STATUS_UNSUCCESSFUL
;
210 static void free_ticket_info(struct ticket_info
*info
)
214 for (i
= 0; i
< info
->count
; i
++)
216 heap_free(info
->info
[i
].RealmName
.Buffer
);
217 heap_free(info
->info
[i
].ServerName
.Buffer
);
220 heap_free(info
->info
);
223 static WCHAR
*utf8_to_wstr(const char *utf8
)
228 len
= MultiByteToWideChar(CP_UTF8
, 0, utf8
, -1, NULL
, 0);
229 wstr
= heap_alloc(len
* sizeof(WCHAR
));
231 MultiByteToWideChar(CP_UTF8
, 0, utf8
, -1, wstr
, len
);
236 static NTSTATUS
copy_tickets_from_cache(krb5_context context
, krb5_ccache cache
, struct ticket_info
*info
)
239 krb5_cc_cursor cursor
;
240 krb5_error_code error
;
241 krb5_creds credentials
;
243 char *name_with_realm
, *name_without_realm
, *realm_name
;
244 WCHAR
*realm_nameW
, *name_without_realmW
;
246 error
= p_krb5_cc_start_seq_get(context
, cache
, &cursor
);
247 if (error
) return krb5_error_to_status(error
);
251 error
= p_krb5_cc_next_cred(context
, cache
, &cursor
, &credentials
);
254 if (error
== KRB5_CC_END
)
255 status
= STATUS_SUCCESS
;
257 status
= krb5_error_to_status(error
);
261 if (p_krb5_is_config_principal(context
, credentials
.server
))
263 p_krb5_free_cred_contents(context
, &credentials
);
267 if (info
->count
== info
->allocated
)
269 KERB_TICKET_CACHE_INFO
*new_info
;
274 new_allocated
= info
->allocated
* 2;
275 new_info
= heap_realloc(info
->info
, sizeof(*new_info
) * new_allocated
);
280 new_info
= heap_alloc(sizeof(*new_info
) * new_allocated
);
284 p_krb5_free_cred_contents(context
, &credentials
);
285 status
= STATUS_NO_MEMORY
;
289 info
->info
= new_info
;
290 info
->allocated
= new_allocated
;
293 error
= p_krb5_unparse_name_flags(context
, credentials
.server
, 0, &name_with_realm
);
296 p_krb5_free_cred_contents(context
, &credentials
);
297 status
= krb5_error_to_status(error
);
301 TRACE("name_with_realm: %s\n", debugstr_a(name_with_realm
));
303 error
= p_krb5_unparse_name_flags(context
, credentials
.server
,
304 KRB5_PRINCIPAL_UNPARSE_NO_REALM
, &name_without_realm
);
307 p_krb5_free_unparsed_name(context
, name_with_realm
);
308 p_krb5_free_cred_contents(context
, &credentials
);
309 status
= krb5_error_to_status(error
);
313 TRACE("name_without_realm: %s\n", debugstr_a(name_without_realm
));
315 name_without_realmW
= utf8_to_wstr(name_without_realm
);
316 RtlInitUnicodeString(&info
->info
[info
->count
].ServerName
, name_without_realmW
);
318 realm_name
= strchr(name_with_realm
, '@');
321 ERR("wrong name with realm %s\n", debugstr_a(name_with_realm
));
322 realm_name
= name_with_realm
;
327 /* realm_name - now contains only realm! */
329 realm_nameW
= utf8_to_wstr(realm_name
);
330 RtlInitUnicodeString(&info
->info
[info
->count
].RealmName
, realm_nameW
);
332 if (!credentials
.times
.starttime
)
333 credentials
.times
.starttime
= credentials
.times
.authtime
;
335 /* TODO: if krb5_is_config_principal = true */
336 RtlSecondsSince1970ToTime(credentials
.times
.starttime
, &info
->info
[info
->count
].StartTime
);
337 RtlSecondsSince1970ToTime(credentials
.times
.endtime
, &info
->info
[info
->count
].EndTime
);
338 RtlSecondsSince1970ToTime(credentials
.times
.renew_till
, &info
->info
[info
->count
].RenewTime
);
340 info
->info
[info
->count
].TicketFlags
= credentials
.ticket_flags
;
342 error
= p_krb5_decode_ticket(&credentials
.ticket
, &ticket
);
344 p_krb5_free_unparsed_name(context
, name_with_realm
);
345 p_krb5_free_unparsed_name(context
, name_without_realm
);
346 p_krb5_free_cred_contents(context
, &credentials
);
350 status
= krb5_error_to_status(error
);
354 info
->info
[info
->count
].EncryptionType
= ticket
->enc_part
.enctype
;
356 p_krb5_free_ticket(context
, ticket
);
361 p_krb5_cc_end_seq_get(context
, cache
, &cursor
);
366 static inline void init_client_us(UNICODE_STRING
*dst
, void *client_ws
, const UNICODE_STRING
*src
)
368 dst
->Buffer
= client_ws
;
369 dst
->Length
= src
->Length
;
370 dst
->MaximumLength
= src
->MaximumLength
;
373 static NTSTATUS
copy_to_client(PLSA_CLIENT_REQUEST lsa_req
, struct ticket_info
*info
, void **out
, ULONG
*out_size
)
377 SIZE_T size
, client_str_off
;
378 char *client_resp
, *client_ticket
, *client_str
;
379 KERB_QUERY_TKT_CACHE_RESPONSE resp
;
381 size
= sizeof(KERB_QUERY_TKT_CACHE_RESPONSE
);
382 if (info
->count
!= 0)
383 size
+= (info
->count
- 1) * sizeof(KERB_TICKET_CACHE_INFO
);
385 client_str_off
= size
;
387 for (i
= 0; i
< info
->count
; i
++)
389 size
+= info
->info
[i
].RealmName
.MaximumLength
;
390 size
+= info
->info
[i
].ServerName
.MaximumLength
;
393 status
= lsa_dispatch
.AllocateClientBuffer(lsa_req
, size
, (void **)&client_resp
);
394 if (status
!= STATUS_SUCCESS
) return status
;
396 resp
.MessageType
= KerbQueryTicketCacheMessage
;
397 resp
.CountOfTickets
= info
->count
;
398 size
= FIELD_OFFSET(KERB_QUERY_TKT_CACHE_RESPONSE
, Tickets
);
399 status
= lsa_dispatch
.CopyToClientBuffer(lsa_req
, size
, client_resp
, &resp
);
400 if (status
!= STATUS_SUCCESS
) goto fail
;
405 *out_size
= sizeof(resp
);
406 return STATUS_SUCCESS
;
411 client_ticket
= client_resp
+ size
;
412 client_str
= client_resp
+ client_str_off
;
414 for (i
= 0; i
< info
->count
; i
++)
416 KERB_TICKET_CACHE_INFO ticket
;
418 ticket
= info
->info
[i
];
420 init_client_us(&ticket
.RealmName
, client_str
, &info
->info
[i
].RealmName
);
422 size
= info
->info
[i
].RealmName
.MaximumLength
;
423 status
= lsa_dispatch
.CopyToClientBuffer(lsa_req
, size
, client_str
, info
->info
[i
].RealmName
.Buffer
);
424 if (status
!= STATUS_SUCCESS
) goto fail
;
428 init_client_us(&ticket
.ServerName
, client_str
, &info
->info
[i
].ServerName
);
430 size
= info
->info
[i
].ServerName
.MaximumLength
;
431 status
= lsa_dispatch
.CopyToClientBuffer(lsa_req
, size
, client_str
, info
->info
[i
].ServerName
.Buffer
);
432 if (status
!= STATUS_SUCCESS
) goto fail
;
436 status
= lsa_dispatch
.CopyToClientBuffer(lsa_req
, sizeof(ticket
), client_ticket
, &ticket
);
437 if (status
!= STATUS_SUCCESS
) goto fail
;
439 client_ticket
+= sizeof(ticket
);
440 *out_size
+= sizeof(ticket
);
444 return STATUS_SUCCESS
;
447 lsa_dispatch
.FreeClientBuffer(lsa_req
, client_resp
);
451 static NTSTATUS
query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req
, void *in
, ULONG in_len
, void **out
, ULONG
*out_len
)
454 KERB_QUERY_TKT_CACHE_REQUEST
*query
;
455 struct ticket_info info
;
456 krb5_error_code error
;
457 krb5_context context
= NULL
;
458 krb5_cccol_cursor cursor
= NULL
;
461 if (!in
|| in_len
!= sizeof(KERB_QUERY_TKT_CACHE_REQUEST
) || !out
|| !out_len
)
462 return STATUS_INVALID_PARAMETER
;
464 query
= (KERB_QUERY_TKT_CACHE_REQUEST
*)in
;
466 if (query
->LogonId
.HighPart
!= 0 || query
->LogonId
.LowPart
!= 0)
467 return STATUS_ACCESS_DENIED
;
473 error
= p_krb5_init_context(&context
);
476 status
= krb5_error_to_status(error
);
480 error
= p_krb5_cccol_cursor_new(context
, &cursor
);
483 status
= krb5_error_to_status(error
);
489 error
= p_krb5_cccol_cursor_next(context
, cursor
, &cache
);
492 status
= krb5_error_to_status(error
);
497 status
= copy_tickets_from_cache(context
, cache
, &info
);
499 p_krb5_cc_close(context
, cache
);
501 if (status
!= STATUS_SUCCESS
)
505 status
= copy_to_client(lsa_req
, &info
, out
, out_len
);
509 p_krb5_cccol_cursor_free(context
, &cursor
);
512 p_krb5_free_context(context
);
514 free_ticket_info(&info
);
519 #else /* SONAME_LIBKRB5 */
521 static NTSTATUS
query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req
, void *in
, ULONG in_len
, void **out
, ULONG
*out_len
)
523 FIXME("%p,%p,%u,%p,%p: stub\n", lsa_req
, in
, in_len
, out
, out_len
);
524 return STATUS_NOT_IMPLEMENTED
;
527 #endif /* SONAME_LIBKRB5 */
529 static NTSTATUS NTAPI
kerberos_LsaApCallPackageUntrusted(PLSA_CLIENT_REQUEST request
,
530 PVOID in_buffer
, PVOID client_buffer_base
, ULONG in_buffer_length
,
531 PVOID
*out_buffer
, PULONG out_buffer_length
, PNTSTATUS status
)
533 KERB_PROTOCOL_MESSAGE_TYPE msg
;
535 TRACE("%p,%p,%p,%u,%p,%p,%p\n", request
, in_buffer
, client_buffer_base
,
536 in_buffer_length
, out_buffer
, out_buffer_length
, status
);
538 if (!in_buffer
|| in_buffer_length
< sizeof(msg
))
539 return STATUS_INVALID_PARAMETER
;
541 msg
= *(KERB_PROTOCOL_MESSAGE_TYPE
*)in_buffer
;
545 case KerbQueryTicketCacheMessage
:
546 *status
= query_ticket_cache(request
, in_buffer
, in_buffer_length
, out_buffer
, out_buffer_length
);
549 case KerbRetrieveTicketMessage
:
550 FIXME("KerbRetrieveTicketMessage stub\n");
551 *status
= STATUS_NOT_IMPLEMENTED
;
554 case KerbPurgeTicketCacheMessage
:
555 FIXME("KerbPurgeTicketCacheMessage stub\n");
556 *status
= STATUS_NOT_IMPLEMENTED
;
559 default: /* All other requests should call LsaApCallPackage */
560 WARN("%u => access denied\n", msg
);
561 *status
= STATUS_ACCESS_DENIED
;
568 static NTSTATUS NTAPI
kerberos_SpGetInfo(SecPkgInfoW
*info
)
572 /* LSA will make a copy before forwarding the structure, so
573 * it's safe to put pointers to dynamic or constant data there.
577 return STATUS_SUCCESS
;
580 #ifdef SONAME_LIBGSSAPI_KRB5
582 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
583 static void *libgssapi_krb5_handle
;
585 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
586 MAKE_FUNCPTR(gss_accept_sec_context
);
587 MAKE_FUNCPTR(gss_acquire_cred
);
588 MAKE_FUNCPTR(gss_delete_sec_context
);
589 MAKE_FUNCPTR(gss_display_status
);
590 MAKE_FUNCPTR(gss_get_mic
);
591 MAKE_FUNCPTR(gss_import_name
);
592 MAKE_FUNCPTR(gss_init_sec_context
);
593 MAKE_FUNCPTR(gss_release_buffer
);
594 MAKE_FUNCPTR(gss_release_cred
);
595 MAKE_FUNCPTR(gss_release_iov_buffer
);
596 MAKE_FUNCPTR(gss_release_name
);
597 MAKE_FUNCPTR(gss_unwrap_iov
);
598 MAKE_FUNCPTR(gss_verify_mic
);
599 MAKE_FUNCPTR(gss_wrap_iov
);
602 static BOOL
load_gssapi_krb5(void)
604 if (!(libgssapi_krb5_handle
= wine_dlopen( SONAME_LIBGSSAPI_KRB5
, RTLD_NOW
, NULL
, 0 )))
606 ERR_(winediag
)( "Failed to load libgssapi_krb5, Kerberos SSP support will not be available.\n" );
610 #define LOAD_FUNCPTR(f) \
611 if (!(p##f = wine_dlsym( libgssapi_krb5_handle, #f, NULL, 0 ))) \
613 ERR( "Failed to load %s\n", #f ); \
617 LOAD_FUNCPTR(gss_accept_sec_context
)
618 LOAD_FUNCPTR(gss_acquire_cred
)
619 LOAD_FUNCPTR(gss_delete_sec_context
)
620 LOAD_FUNCPTR(gss_display_status
)
621 LOAD_FUNCPTR(gss_get_mic
)
622 LOAD_FUNCPTR(gss_import_name
)
623 LOAD_FUNCPTR(gss_init_sec_context
)
624 LOAD_FUNCPTR(gss_release_buffer
)
625 LOAD_FUNCPTR(gss_release_cred
)
626 LOAD_FUNCPTR(gss_release_iov_buffer
)
627 LOAD_FUNCPTR(gss_release_name
)
628 LOAD_FUNCPTR(gss_unwrap_iov
)
629 LOAD_FUNCPTR(gss_verify_mic
)
630 LOAD_FUNCPTR(gss_wrap_iov
)
636 wine_dlclose( libgssapi_krb5_handle
, NULL
, 0 );
637 libgssapi_krb5_handle
= NULL
;
641 static void unload_gssapi_krb5(void)
643 wine_dlclose( libgssapi_krb5_handle
, NULL
, 0 );
644 libgssapi_krb5_handle
= NULL
;
647 static inline gss_cred_id_t
credhandle_sspi_to_gss( LSA_SEC_HANDLE cred
)
649 if (!cred
) return GSS_C_NO_CREDENTIAL
;
650 return (gss_cred_id_t
)cred
;
653 static inline void credhandle_gss_to_sspi( gss_cred_id_t handle
, LSA_SEC_HANDLE
*cred
)
655 *cred
= (LSA_SEC_HANDLE
)handle
;
658 static inline gss_ctx_id_t
ctxthandle_sspi_to_gss( LSA_SEC_HANDLE ctxt
)
660 if (!ctxt
) return GSS_C_NO_CONTEXT
;
661 return (gss_ctx_id_t
)ctxt
;
664 static inline void ctxthandle_gss_to_sspi( gss_ctx_id_t handle
, LSA_SEC_HANDLE
*ctxt
)
666 *ctxt
= (LSA_SEC_HANDLE
)handle
;
669 static SECURITY_STATUS
status_gss_to_sspi( OM_uint32 status
)
673 case GSS_S_COMPLETE
: return SEC_E_OK
;
674 case GSS_S_BAD_MECH
: return SEC_E_SECPKG_NOT_FOUND
;
675 case GSS_S_BAD_SIG
: return SEC_E_MESSAGE_ALTERED
;
676 case GSS_S_NO_CRED
: return SEC_E_NO_CREDENTIALS
;
677 case GSS_S_NO_CONTEXT
: return SEC_E_INVALID_HANDLE
;
678 case GSS_S_DEFECTIVE_TOKEN
: return SEC_E_INVALID_TOKEN
;
679 case GSS_S_DEFECTIVE_CREDENTIAL
: return SEC_E_NO_CREDENTIALS
;
680 case GSS_S_CREDENTIALS_EXPIRED
: return SEC_E_CONTEXT_EXPIRED
;
681 case GSS_S_CONTEXT_EXPIRED
: return SEC_E_CONTEXT_EXPIRED
;
682 case GSS_S_BAD_QOP
: return SEC_E_QOP_NOT_SUPPORTED
;
683 case GSS_S_CONTINUE_NEEDED
: return SEC_I_CONTINUE_NEEDED
;
684 case GSS_S_DUPLICATE_TOKEN
: return SEC_E_INVALID_TOKEN
;
685 case GSS_S_OLD_TOKEN
: return SEC_E_INVALID_TOKEN
;
686 case GSS_S_UNSEQ_TOKEN
: return SEC_E_OUT_OF_SEQUENCE
;
687 case GSS_S_GAP_TOKEN
: return SEC_E_OUT_OF_SEQUENCE
;
690 FIXME( "couldn't convert status 0x%08x to SECURITY_STATUS\n", status
);
691 return SEC_E_INTERNAL_ERROR
;
695 static void trace_gss_status_ex( OM_uint32 code
, int type
)
697 OM_uint32 ret
, minor_status
;
699 OM_uint32 message_context
= 0;
703 ret
= pgss_display_status( &minor_status
, code
, type
, GSS_C_NULL_OID
, &message_context
, &buf
);
706 TRACE( "gss_display_status(0x%08x,%d) returned %08x minor status %08x\n",
707 code
, type
, ret
, minor_status
);
710 TRACE( "GSS-API error: 0x%08x: %s\n", code
, debugstr_an(buf
.value
, buf
.length
) );
711 pgss_release_buffer( &minor_status
, &buf
);
713 if (!message_context
) return;
717 static void trace_gss_status( OM_uint32 major_status
, OM_uint32 minor_status
)
719 if (TRACE_ON(kerberos
))
721 trace_gss_status_ex( major_status
, GSS_C_GSS_CODE
);
722 trace_gss_status_ex( minor_status
, GSS_C_MECH_CODE
);
726 static void expirytime_gss_to_sspi( OM_uint32 expirytime
, TimeStamp
*timestamp
)
732 GetLocalTime( &time
);
733 SystemTimeToFileTime( &time
, &filetime
);
734 tmp
.QuadPart
= ((ULONGLONG
)filetime
.dwLowDateTime
| (ULONGLONG
)filetime
.dwHighDateTime
<< 32) + expirytime
;
735 timestamp
->LowPart
= tmp
.QuadPart
;
736 timestamp
->HighPart
= tmp
.QuadPart
>> 32;
739 static SECURITY_STATUS
name_sspi_to_gss( const UNICODE_STRING
*name_str
, gss_name_t
*name
)
741 OM_uint32 ret
, minor_status
;
742 gss_OID type
= GSS_C_NO_OID
; /* FIXME: detect the appropriate value for this ourselves? */
745 buf
.length
= WideCharToMultiByte( CP_UNIXCP
, 0, name_str
->Buffer
, name_str
->Length
/ sizeof(WCHAR
), NULL
, 0, NULL
, NULL
) + 1;
746 if (!(buf
.value
= heap_alloc( buf
.length
))) return SEC_E_INSUFFICIENT_MEMORY
;
747 WideCharToMultiByte( CP_UNIXCP
, 0, name_str
->Buffer
, name_str
->Length
/ sizeof(WCHAR
), buf
.value
, buf
.length
, NULL
, NULL
);
750 ret
= pgss_import_name( &minor_status
, &buf
, type
, name
);
751 TRACE( "gss_import_name returned %08x minor status %08x\n", ret
, minor_status
);
752 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
754 heap_free( buf
.value
);
755 return status_gss_to_sspi( ret
);
758 static ULONG
flags_isc_req_to_gss( ULONG flags
)
760 ULONG ret
= GSS_C_DCE_STYLE
;
761 if (flags
& ISC_REQ_DELEGATE
) ret
|= GSS_C_DELEG_FLAG
;
762 if (flags
& ISC_REQ_MUTUAL_AUTH
) ret
|= GSS_C_MUTUAL_FLAG
;
763 if (flags
& ISC_REQ_REPLAY_DETECT
) ret
|= GSS_C_REPLAY_FLAG
;
764 if (flags
& ISC_REQ_SEQUENCE_DETECT
) ret
|= GSS_C_SEQUENCE_FLAG
;
765 if (flags
& ISC_REQ_CONFIDENTIALITY
) ret
|= GSS_C_CONF_FLAG
;
766 if (flags
& ISC_REQ_INTEGRITY
) ret
|= GSS_C_INTEG_FLAG
;
767 if (flags
& ISC_REQ_NULL_SESSION
) ret
|= GSS_C_ANON_FLAG
;
771 static ULONG
flags_gss_to_isc_ret( ULONG flags
)
774 if (flags
& GSS_C_DELEG_FLAG
) ret
|= ISC_RET_DELEGATE
;
775 if (flags
& GSS_C_MUTUAL_FLAG
) ret
|= ISC_RET_MUTUAL_AUTH
;
776 if (flags
& GSS_C_REPLAY_FLAG
) ret
|= ISC_RET_REPLAY_DETECT
;
777 if (flags
& GSS_C_SEQUENCE_FLAG
) ret
|= ISC_RET_SEQUENCE_DETECT
;
778 if (flags
& GSS_C_CONF_FLAG
) ret
|= ISC_RET_CONFIDENTIALITY
;
779 if (flags
& GSS_C_INTEG_FLAG
) ret
|= ISC_RET_INTEGRITY
;
780 if (flags
& GSS_C_ANON_FLAG
) ret
|= ISC_RET_NULL_SESSION
;
784 static ULONG
flags_gss_to_asc_ret( ULONG flags
)
787 if (flags
& GSS_C_DELEG_FLAG
) ret
|= ASC_RET_DELEGATE
;
788 if (flags
& GSS_C_MUTUAL_FLAG
) ret
|= ASC_RET_MUTUAL_AUTH
;
789 if (flags
& GSS_C_REPLAY_FLAG
) ret
|= ASC_RET_REPLAY_DETECT
;
790 if (flags
& GSS_C_SEQUENCE_FLAG
) ret
|= ASC_RET_SEQUENCE_DETECT
;
791 if (flags
& GSS_C_CONF_FLAG
) ret
|= ASC_RET_CONFIDENTIALITY
;
792 if (flags
& GSS_C_INTEG_FLAG
) ret
|= ASC_RET_INTEGRITY
;
793 if (flags
& GSS_C_ANON_FLAG
) ret
|= ASC_RET_NULL_SESSION
;
797 static int get_buffer_index( SecBufferDesc
*desc
, DWORD type
)
800 if (!desc
) return -1;
801 for (i
= 0; i
< desc
->cBuffers
; i
++)
803 if (desc
->pBuffers
[i
].BufferType
== type
) return i
;
807 #endif /* SONAME_LIBGSSAPI_KRB5 */
809 static NTSTATUS NTAPI
kerberos_SpAcquireCredentialsHandle(
810 UNICODE_STRING
*principal_us
, ULONG credential_use
, LUID
*logon_id
, void *auth_data
,
811 void *get_key_fn
, void *get_key_arg
, LSA_SEC_HANDLE
*credential
, TimeStamp
*ts_expiry
)
813 #ifdef SONAME_LIBGSSAPI_KRB5
814 OM_uint32 ret
, minor_status
, expiry_time
;
815 gss_name_t principal
= GSS_C_NO_NAME
;
816 gss_cred_usage_t cred_usage
;
817 gss_cred_id_t cred_handle
;
819 TRACE( "(%s 0x%08x %p %p %p %p %p %p)\n", debugstr_us(principal_us
), credential_use
,
820 logon_id
, auth_data
, get_key_fn
, get_key_arg
, credential
, ts_expiry
);
824 FIXME( "specific credentials not supported\n" );
825 return SEC_E_UNKNOWN_CREDENTIALS
;
828 switch (credential_use
)
830 case SECPKG_CRED_INBOUND
:
831 cred_usage
= GSS_C_ACCEPT
;
833 case SECPKG_CRED_OUTBOUND
:
834 cred_usage
= GSS_C_INITIATE
;
836 case SECPKG_CRED_BOTH
:
837 cred_usage
= GSS_C_BOTH
;
840 return SEC_E_UNKNOWN_CREDENTIALS
;
843 if (principal_us
&& ((ret
= name_sspi_to_gss( principal_us
, &principal
)) != SEC_E_OK
)) return ret
;
845 ret
= pgss_acquire_cred( &minor_status
, principal
, GSS_C_INDEFINITE
, GSS_C_NULL_OID_SET
, cred_usage
,
846 &cred_handle
, NULL
, &expiry_time
);
847 TRACE( "gss_acquire_cred returned %08x minor status %08x\n", ret
, minor_status
);
848 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
849 if (ret
== GSS_S_COMPLETE
)
851 credhandle_gss_to_sspi( cred_handle
, credential
);
852 expirytime_gss_to_sspi( expiry_time
, ts_expiry
);
855 if (principal
!= GSS_C_NO_NAME
) pgss_release_name( &minor_status
, &principal
);
857 return status_gss_to_sspi( ret
);
859 FIXME( "(%s 0x%08x %p %p %p %p %p %p)\n", debugstr_us(principal_us
), credential_use
,
860 logon_id
, auth_data
, get_key_fn
, get_key_arg
, credential
, ts_expiry
);
861 FIXME( "Wine was built without Kerberos support.\n" );
862 return SEC_E_UNSUPPORTED_FUNCTION
;
866 static NTSTATUS NTAPI
kerberos_SpFreeCredentialsHandle( LSA_SEC_HANDLE credential
)
868 #ifdef SONAME_LIBGSSAPI_KRB5
869 OM_uint32 ret
, minor_status
;
870 gss_cred_id_t cred_handle
;
872 TRACE( "(%lx)\n", credential
);
874 if (!credential
) return SEC_E_INVALID_HANDLE
;
875 if (!(cred_handle
= credhandle_sspi_to_gss( credential
))) return SEC_E_OK
;
877 ret
= pgss_release_cred( &minor_status
, &cred_handle
);
878 TRACE( "gss_release_cred returned %08x minor status %08x\n", ret
, minor_status
);
879 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
881 return status_gss_to_sspi( ret
);
883 FIXME( "(%lx)\n", credential
);
884 return SEC_E_UNSUPPORTED_FUNCTION
;
888 static NTSTATUS NTAPI
kerberos_SpInitLsaModeContext( LSA_SEC_HANDLE credential
, LSA_SEC_HANDLE context
,
889 UNICODE_STRING
*target_name
, ULONG context_req
, ULONG target_data_rep
, SecBufferDesc
*input
,
890 LSA_SEC_HANDLE
*new_context
, SecBufferDesc
*output
, ULONG
*context_attr
, TimeStamp
*ts_expiry
,
891 BOOLEAN
*mapped_context
, SecBuffer
*context_data
)
893 #ifdef SONAME_LIBGSSAPI_KRB5
894 static const ULONG supported
= ISC_REQ_CONFIDENTIALITY
| ISC_REQ_INTEGRITY
| ISC_REQ_SEQUENCE_DETECT
|
895 ISC_REQ_REPLAY_DETECT
| ISC_REQ_MUTUAL_AUTH
;
896 OM_uint32 ret
, minor_status
, ret_flags
, expiry_time
, req_flags
= flags_isc_req_to_gss( context_req
);
897 gss_cred_id_t cred_handle
;
898 gss_ctx_id_t ctxt_handle
;
899 gss_buffer_desc input_token
, output_token
;
900 gss_name_t target
= GSS_C_NO_NAME
;
903 TRACE( "(%lx %lx %s 0x%08x %u %p %p %p %p %p %p %p)\n", credential
, context
, debugstr_us(target_name
),
904 context_req
, target_data_rep
, input
, new_context
, output
, context_attr
, ts_expiry
,
905 mapped_context
, context_data
);
906 if (context_req
& ~supported
)
907 FIXME( "flags 0x%08x not supported\n", context_req
& ~supported
);
909 if (!context
&& !input
&& !credential
) return SEC_E_INVALID_HANDLE
;
910 cred_handle
= credhandle_sspi_to_gss( credential
);
911 ctxt_handle
= ctxthandle_sspi_to_gss( context
);
913 if (!input
) input_token
.length
= 0;
916 if ((idx
= get_buffer_index( input
, SECBUFFER_TOKEN
)) == -1)
917 input_token
.length
= 0;
920 input_token
.length
= input
->pBuffers
[idx
].cbBuffer
;
921 input_token
.value
= input
->pBuffers
[idx
].pvBuffer
;
925 if ((idx
= get_buffer_index( output
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
926 output_token
.length
= 0;
927 output_token
.value
= NULL
;
929 if (target_name
&& ((ret
= name_sspi_to_gss( target_name
, &target
)) != SEC_E_OK
)) return ret
;
931 ret
= pgss_init_sec_context( &minor_status
, cred_handle
, &ctxt_handle
, target
, GSS_C_NO_OID
, req_flags
, 0,
932 GSS_C_NO_CHANNEL_BINDINGS
, &input_token
, NULL
, &output_token
, &ret_flags
,
934 TRACE( "gss_init_sec_context returned %08x minor status %08x ret_flags %08x\n", ret
, minor_status
, ret_flags
);
935 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
936 if (ret
== GSS_S_COMPLETE
|| ret
== GSS_S_CONTINUE_NEEDED
)
938 if (output_token
.length
> output
->pBuffers
[idx
].cbBuffer
) /* FIXME: check if larger buffer exists */
940 TRACE( "buffer too small %lu > %u\n", (SIZE_T
)output_token
.length
, output
->pBuffers
[idx
].cbBuffer
);
941 pgss_release_buffer( &minor_status
, &output_token
);
942 pgss_delete_sec_context( &minor_status
, &ctxt_handle
, GSS_C_NO_BUFFER
);
943 return SEC_E_INCOMPLETE_MESSAGE
;
945 output
->pBuffers
[idx
].cbBuffer
= output_token
.length
;
946 memcpy( output
->pBuffers
[idx
].pvBuffer
, output_token
.value
, output_token
.length
);
947 pgss_release_buffer( &minor_status
, &output_token
);
949 ctxthandle_gss_to_sspi( ctxt_handle
, new_context
);
950 if (context_attr
) *context_attr
= flags_gss_to_isc_ret( ret_flags
);
951 expirytime_gss_to_sspi( expiry_time
, ts_expiry
);
954 if (target
!= GSS_C_NO_NAME
) pgss_release_name( &minor_status
, &target
);
956 /* we do support user mode SSP/AP functions */
957 *mapped_context
= TRUE
;
958 /* FIXME: initialize context_data */
960 return status_gss_to_sspi( ret
);
962 FIXME( "(%lx %lx %s 0x%08x %u %p %p %p %p %p %p %p)\n", credential
, context
, debugstr_us(target_name
),
963 context_req
, target_data_rep
, input
, new_context
, output
, context_attr
, ts_expiry
,
964 mapped_context
, context_data
);
965 return SEC_E_UNSUPPORTED_FUNCTION
;
969 static NTSTATUS NTAPI
kerberos_SpAcceptLsaModeContext( LSA_SEC_HANDLE credential
, LSA_SEC_HANDLE context
,
970 SecBufferDesc
*input
, ULONG context_req
, ULONG target_data_rep
, LSA_SEC_HANDLE
*new_context
,
971 SecBufferDesc
*output
, ULONG
*context_attr
, TimeStamp
*ts_expiry
, BOOLEAN
*mapped_context
, SecBuffer
*context_data
)
973 #ifdef SONAME_LIBGSSAPI_KRB5
974 OM_uint32 ret
, minor_status
, ret_flags
, expiry_time
;
975 gss_cred_id_t cred_handle
;
976 gss_ctx_id_t ctxt_handle
;
977 gss_buffer_desc input_token
, output_token
;
978 gss_name_t target
= GSS_C_NO_NAME
;
981 TRACE( "(%lx %lx 0x%08x %u %p %p %p %p %p %p %p)\n", credential
, context
, context_req
,
982 target_data_rep
, input
, new_context
, output
, context_attr
, ts_expiry
,
983 mapped_context
, context_data
);
984 if (context_req
) FIXME( "ignoring flags 0x%08x\n", context_req
);
986 if (!context
&& !input
&& !credential
) return SEC_E_INVALID_HANDLE
;
987 cred_handle
= credhandle_sspi_to_gss( credential
);
988 ctxt_handle
= ctxthandle_sspi_to_gss( context
);
990 if (!input
) input_token
.length
= 0;
993 if ((idx
= get_buffer_index( input
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
994 input_token
.length
= input
->pBuffers
[idx
].cbBuffer
;
995 input_token
.value
= input
->pBuffers
[idx
].pvBuffer
;
998 if ((idx
= get_buffer_index( output
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
999 output_token
.length
= 0;
1000 output_token
.value
= NULL
;
1002 ret
= pgss_accept_sec_context( &minor_status
, &ctxt_handle
, cred_handle
, &input_token
, GSS_C_NO_CHANNEL_BINDINGS
,
1003 &target
, NULL
, &output_token
, &ret_flags
, &expiry_time
, NULL
);
1004 TRACE( "gss_accept_sec_context returned %08x minor status %08x ctxt_handle %p\n", ret
, minor_status
, ctxt_handle
);
1005 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
1006 if (ret
== GSS_S_COMPLETE
|| ret
== GSS_S_CONTINUE_NEEDED
)
1008 if (output_token
.length
> output
->pBuffers
[idx
].cbBuffer
) /* FIXME: check if larger buffer exists */
1010 TRACE( "buffer too small %lu > %u\n", (SIZE_T
)output_token
.length
, output
->pBuffers
[idx
].cbBuffer
);
1011 pgss_release_buffer( &minor_status
, &output_token
);
1012 pgss_delete_sec_context( &minor_status
, &ctxt_handle
, GSS_C_NO_BUFFER
);
1013 return SEC_E_BUFFER_TOO_SMALL
;
1015 output
->pBuffers
[idx
].cbBuffer
= output_token
.length
;
1016 memcpy( output
->pBuffers
[idx
].pvBuffer
, output_token
.value
, output_token
.length
);
1017 pgss_release_buffer( &minor_status
, &output_token
);
1019 ctxthandle_gss_to_sspi( ctxt_handle
, new_context
);
1020 if (context_attr
) *context_attr
= flags_gss_to_asc_ret( ret_flags
);
1021 expirytime_gss_to_sspi( expiry_time
, ts_expiry
);
1024 /* we do support user mode SSP/AP functions */
1025 *mapped_context
= TRUE
;
1026 /* FIXME: initialize context_data */
1028 return status_gss_to_sspi( ret
);
1030 FIXME( "(%lx %lx 0x%08x %u %p %p %p %p %p %p %p)\n", credential
, context
, context_req
,
1031 target_data_rep
, input
, new_context
, output
, context_attr
, ts_expiry
,
1032 mapped_context
, context_data
);
1033 return SEC_E_UNSUPPORTED_FUNCTION
;
1037 static NTSTATUS NTAPI
kerberos_SpDeleteContext( LSA_SEC_HANDLE context
)
1039 #ifdef SONAME_LIBGSSAPI_KRB5
1040 OM_uint32 ret
, minor_status
;
1041 gss_ctx_id_t ctxt_handle
;
1043 TRACE( "(%lx)\n", context
);
1044 if (!context
) return SEC_E_INVALID_HANDLE
;
1045 if (!(ctxt_handle
= ctxthandle_sspi_to_gss( context
))) return SEC_E_OK
;
1047 ret
= pgss_delete_sec_context( &minor_status
, &ctxt_handle
, GSS_C_NO_BUFFER
);
1048 TRACE( "gss_delete_sec_context returned %08x minor status %08x\n", ret
, minor_status
);
1049 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
1051 return status_gss_to_sspi( ret
);
1053 FIXME( "(%lx)\n", context
);
1054 return SEC_E_UNSUPPORTED_FUNCTION
;
1058 static NTSTATUS NTAPI
kerberos_SpQueryContextAttributes( LSA_SEC_HANDLE context
, ULONG attribute
, void *buffer
)
1060 TRACE( "(%lx %u %p)\n", context
, attribute
, buffer
);
1062 if (!context
) return SEC_E_INVALID_HANDLE
;
1066 #define X(x) case (x) : FIXME(#x" stub\n"); break
1067 X(SECPKG_ATTR_ACCESS_TOKEN
);
1068 X(SECPKG_ATTR_AUTHORITY
);
1069 X(SECPKG_ATTR_DCE_INFO
);
1070 X(SECPKG_ATTR_KEY_INFO
);
1071 X(SECPKG_ATTR_LIFESPAN
);
1072 X(SECPKG_ATTR_NAMES
);
1073 X(SECPKG_ATTR_NATIVE_NAMES
);
1074 X(SECPKG_ATTR_PACKAGE_INFO
);
1075 X(SECPKG_ATTR_PASSWORD_EXPIRY
);
1076 X(SECPKG_ATTR_SESSION_KEY
);
1077 X(SECPKG_ATTR_STREAM_SIZES
);
1078 X(SECPKG_ATTR_TARGET_INFORMATION
);
1079 case SECPKG_ATTR_SIZES
:
1081 SecPkgContext_Sizes
*sizes
= (SecPkgContext_Sizes
*)buffer
;
1082 sizes
->cbMaxToken
= KERBEROS_MAX_BUF
;
1083 sizes
->cbMaxSignature
= 37;
1084 sizes
->cbBlockSize
= 1;
1085 sizes
->cbSecurityTrailer
= 49;
1088 case SECPKG_ATTR_NEGOTIATION_INFO
:
1090 SecPkgContext_NegotiationInfoW
*info
= (SecPkgContext_NegotiationInfoW
*)buffer
;
1091 info
->PackageInfo
= (SecPkgInfoW
*)&infoW
;
1092 info
->NegotiationState
= SECPKG_NEGOTIATION_COMPLETE
;
1097 FIXME( "unknown attribute %u\n", attribute
);
1101 return SEC_E_UNSUPPORTED_FUNCTION
;
1104 static NTSTATUS NTAPI
kerberos_SpInitialize(ULONG_PTR package_id
, SECPKG_PARAMETERS
*params
,
1105 LSA_SECPKG_FUNCTION_TABLE
*lsa_function_table
)
1107 TRACE("%lu,%p,%p\n", package_id
, params
, lsa_function_table
);
1109 #ifdef SONAME_LIBGSSAPI_KRB5
1110 if (load_gssapi_krb5()) return STATUS_SUCCESS
;
1113 return STATUS_UNSUCCESSFUL
;
1116 static NTSTATUS NTAPI
kerberos_SpShutdown(void)
1120 #ifdef SONAME_LIBGSSAPI_KRB5
1121 unload_gssapi_krb5();
1124 return STATUS_SUCCESS
;
1127 static SECPKG_FUNCTION_TABLE kerberos_table
=
1129 kerberos_LsaApInitializePackage
, /* InitializePackage */
1130 NULL
, /* LsaLogonUser */
1131 NULL
, /* CallPackage */
1132 NULL
, /* LogonTerminated */
1133 kerberos_LsaApCallPackageUntrusted
, /* CallPackageUntrusted */
1134 NULL
, /* CallPackagePassthrough */
1135 NULL
, /* LogonUserEx */
1136 NULL
, /* LogonUserEx2 */
1137 kerberos_SpInitialize
,
1138 kerberos_SpShutdown
,
1140 NULL
, /* AcceptCredentials */
1141 kerberos_SpAcquireCredentialsHandle
,
1142 NULL
, /* SpQueryCredentialsAttributes */
1143 kerberos_SpFreeCredentialsHandle
,
1144 NULL
, /* SaveCredentials */
1145 NULL
, /* GetCredentials */
1146 NULL
, /* DeleteCredentials */
1147 kerberos_SpInitLsaModeContext
,
1148 kerberos_SpAcceptLsaModeContext
,
1149 kerberos_SpDeleteContext
,
1150 NULL
, /* ApplyControlToken */
1151 NULL
, /* GetUserInfo */
1152 NULL
, /* GetExtendedInformation */
1153 kerberos_SpQueryContextAttributes
,
1154 NULL
, /* SpAddCredentials */
1155 NULL
, /* SetExtendedInformation */
1156 NULL
, /* SetContextAttributes */
1157 NULL
, /* SetCredentialsAttributes */
1158 NULL
, /* ChangeAccountPassword */
1159 NULL
, /* QueryMetaData */
1160 NULL
, /* ExchangeMetaData */
1161 NULL
, /* GetCredUIContext */
1162 NULL
, /* UpdateCredentials */
1163 NULL
, /* ValidateTargetInfo */
1164 NULL
, /* PostLogonUser */
1167 NTSTATUS NTAPI
SpLsaModeInitialize(ULONG lsa_version
, PULONG package_version
,
1168 PSECPKG_FUNCTION_TABLE
*table
, PULONG table_count
)
1170 TRACE("%#x,%p,%p,%p\n", lsa_version
, package_version
, table
, table_count
);
1172 *package_version
= SECPKG_INTERFACE_VERSION
;
1173 *table
= &kerberos_table
;
1176 return STATUS_SUCCESS
;
1179 static NTSTATUS NTAPI
kerberos_SpInstanceInit(ULONG version
, SECPKG_DLL_FUNCTIONS
*dll_function_table
, void **user_functions
)
1181 FIXME("%u,%p,%p: stub\n", version
, dll_function_table
, user_functions
);
1183 return STATUS_SUCCESS
;
1186 static NTSTATUS SEC_ENTRY
kerberos_SpMakeSignature( LSA_SEC_HANDLE context
, ULONG quality_of_protection
,
1187 SecBufferDesc
*message
, ULONG message_seq_no
)
1189 #ifdef SONAME_LIBGSSAPI_KRB5
1190 OM_uint32 ret
, minor_status
;
1191 gss_buffer_desc data_buffer
, token_buffer
;
1192 gss_ctx_id_t ctxt_handle
;
1193 int data_idx
, token_idx
;
1195 TRACE( "(%lx 0x%08x %p %u)\n", context
, quality_of_protection
, message
, message_seq_no
);
1196 if (quality_of_protection
) FIXME( "ignoring quality_of_protection 0x%08x\n", quality_of_protection
);
1197 if (message_seq_no
) FIXME( "ignoring message_seq_no %u\n", message_seq_no
);
1199 if (!context
) return SEC_E_INVALID_HANDLE
;
1200 ctxt_handle
= ctxthandle_sspi_to_gss( context
);
1202 /* FIXME: multiple data buffers, read-only buffers */
1203 if ((data_idx
= get_buffer_index( message
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
1204 data_buffer
.length
= message
->pBuffers
[data_idx
].cbBuffer
;
1205 data_buffer
.value
= message
->pBuffers
[data_idx
].pvBuffer
;
1207 if ((token_idx
= get_buffer_index( message
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
1208 token_buffer
.length
= 0;
1209 token_buffer
.value
= NULL
;
1211 ret
= pgss_get_mic( &minor_status
, ctxt_handle
, GSS_C_QOP_DEFAULT
, &data_buffer
, &token_buffer
);
1212 TRACE( "gss_get_mic returned %08x minor status %08x\n", ret
, minor_status
);
1213 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
1214 if (ret
== GSS_S_COMPLETE
)
1216 memcpy( message
->pBuffers
[token_idx
].pvBuffer
, token_buffer
.value
, token_buffer
.length
);
1217 message
->pBuffers
[token_idx
].cbBuffer
= token_buffer
.length
;
1218 pgss_release_buffer( &minor_status
, &token_buffer
);
1221 return status_gss_to_sspi( ret
);
1223 FIXME( "(%lx 0x%08x %p %u)\n", context
, quality_of_protection
, message
, message_seq_no
);
1224 return SEC_E_UNSUPPORTED_FUNCTION
;
1228 static SECURITY_STATUS SEC_ENTRY
kerberos_SpVerifySignature( LSA_SEC_HANDLE context
, SecBufferDesc
*message
,
1229 ULONG message_seq_no
, ULONG
*quality_of_protection
)
1231 #ifdef SONAME_LIBGSSAPI_KRB5
1232 OM_uint32 ret
, minor_status
;
1233 gss_buffer_desc data_buffer
, token_buffer
;
1234 gss_ctx_id_t ctxt_handle
;
1235 int data_idx
, token_idx
;
1237 TRACE( "(%lx %p %u %p)\n", context
, message
, message_seq_no
, quality_of_protection
);
1238 if (message_seq_no
) FIXME( "ignoring message_seq_no %u\n", message_seq_no
);
1240 if (!context
) return SEC_E_INVALID_HANDLE
;
1241 ctxt_handle
= ctxthandle_sspi_to_gss( context
);
1243 if ((data_idx
= get_buffer_index( message
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
1244 data_buffer
.length
= message
->pBuffers
[data_idx
].cbBuffer
;
1245 data_buffer
.value
= message
->pBuffers
[data_idx
].pvBuffer
;
1247 if ((token_idx
= get_buffer_index( message
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
1248 token_buffer
.length
= message
->pBuffers
[token_idx
].cbBuffer
;
1249 token_buffer
.value
= message
->pBuffers
[token_idx
].pvBuffer
;
1251 ret
= pgss_verify_mic( &minor_status
, ctxt_handle
, &data_buffer
, &token_buffer
, NULL
);
1252 TRACE( "gss_verify_mic returned %08x minor status %08x\n", ret
, minor_status
);
1253 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
1254 if (ret
== GSS_S_COMPLETE
&& quality_of_protection
) *quality_of_protection
= 0;
1256 return status_gss_to_sspi( ret
);
1258 FIXME( "(%lx %p %u %p)\n", context
, message
, message_seq_no
, quality_of_protection
);
1259 return SEC_E_UNSUPPORTED_FUNCTION
;
1263 static NTSTATUS NTAPI
kerberos_SpSealMessage( LSA_SEC_HANDLE context
, ULONG quality_of_protection
,
1264 SecBufferDesc
*message
, ULONG message_seq_no
)
1266 #ifdef SONAME_LIBGSSAPI_KRB5
1267 gss_ctx_id_t ctxt_handle
;
1268 gss_iov_buffer_desc iov
[4];
1269 OM_uint32 ret
, minor_status
;
1270 int token_idx
, data_idx
, conf_state
;
1272 TRACE( "(%lx 0x%08x %p %u)\n", context
, quality_of_protection
, message
, message_seq_no
);
1273 if (quality_of_protection
)
1275 FIXME( "flags %08x not supported\n", quality_of_protection
);
1276 return SEC_E_UNSUPPORTED_FUNCTION
;
1278 if (message_seq_no
) FIXME( "ignoring message_seq_no %u\n", message_seq_no
);
1280 if (!context
) return SEC_E_INVALID_HANDLE
;
1281 ctxt_handle
= ctxthandle_sspi_to_gss( context
);
1283 /* FIXME: multiple data buffers, read-only buffers */
1284 if ((data_idx
= get_buffer_index( message
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
1285 if ((token_idx
= get_buffer_index( message
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
1287 iov
[0].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
1288 iov
[0].buffer
.length
= 0;
1289 iov
[0].buffer
.value
= NULL
;
1291 iov
[1].type
= GSS_IOV_BUFFER_TYPE_DATA
;
1292 iov
[1].buffer
.length
= message
->pBuffers
[data_idx
].cbBuffer
;
1293 iov
[1].buffer
.value
= message
->pBuffers
[data_idx
].pvBuffer
;
1295 iov
[2].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
1296 iov
[2].buffer
.length
= 0;
1297 iov
[2].buffer
.value
= NULL
;
1299 iov
[3].type
= GSS_IOV_BUFFER_TYPE_HEADER
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
1300 iov
[3].buffer
.length
= 0;
1301 iov
[3].buffer
.value
= NULL
;
1303 ret
= pgss_wrap_iov( &minor_status
, ctxt_handle
, 1, GSS_C_QOP_DEFAULT
, &conf_state
, iov
, 4 );
1304 TRACE( "gss_wrap_iov returned %08x minor status %08x\n", ret
, minor_status
);
1305 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
1306 if (ret
== GSS_S_COMPLETE
)
1308 memcpy( message
->pBuffers
[token_idx
].pvBuffer
, iov
[3].buffer
.value
, iov
[3].buffer
.length
);
1309 message
->pBuffers
[token_idx
].cbBuffer
= iov
[3].buffer
.length
;
1310 pgss_release_iov_buffer( &minor_status
, iov
, 4 );
1313 return status_gss_to_sspi( ret
);
1315 FIXME( "(%lx 0x%08x %p %u)\n", context
, quality_of_protection
, message
, message_seq_no
);
1316 return SEC_E_UNSUPPORTED_FUNCTION
;
1320 static NTSTATUS NTAPI
kerberos_SpUnsealMessage( LSA_SEC_HANDLE context
, SecBufferDesc
*message
,
1321 ULONG message_seq_no
, ULONG
*quality_of_protection
)
1323 #ifdef SONAME_LIBGSSAPI_KRB5
1324 gss_ctx_id_t ctxt_handle
;
1325 gss_iov_buffer_desc iov
[4];
1326 OM_uint32 ret
, minor_status
;
1327 int token_idx
, data_idx
, conf_state
;
1329 TRACE( "(%lx %p %u %p)\n", context
, message
, message_seq_no
, quality_of_protection
);
1330 if (message_seq_no
) FIXME( "ignoring message_seq_no %u\n", message_seq_no
);
1332 if (!context
) return SEC_E_INVALID_HANDLE
;
1333 ctxt_handle
= ctxthandle_sspi_to_gss( context
);
1335 if ((data_idx
= get_buffer_index( message
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
1336 if ((token_idx
= get_buffer_index( message
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
1338 iov
[0].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
;
1339 iov
[0].buffer
.length
= 0;
1340 iov
[0].buffer
.value
= NULL
;
1342 iov
[1].type
= GSS_IOV_BUFFER_TYPE_DATA
;
1343 iov
[1].buffer
.length
= message
->pBuffers
[data_idx
].cbBuffer
;
1344 iov
[1].buffer
.value
= message
->pBuffers
[data_idx
].pvBuffer
;
1346 iov
[2].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
;
1347 iov
[2].buffer
.length
= 0;
1348 iov
[2].buffer
.value
= NULL
;
1350 iov
[3].type
= GSS_IOV_BUFFER_TYPE_HEADER
;
1351 iov
[3].buffer
.length
= message
->pBuffers
[token_idx
].cbBuffer
;
1352 iov
[3].buffer
.value
= message
->pBuffers
[token_idx
].pvBuffer
;
1354 ret
= pgss_unwrap_iov( &minor_status
, ctxt_handle
, &conf_state
, NULL
, iov
, 4 );
1355 TRACE( "gss_unwrap_iov returned %08x minor status %08x\n", ret
, minor_status
);
1356 if (GSS_ERROR(ret
)) trace_gss_status( ret
, minor_status
);
1357 if (ret
== GSS_S_COMPLETE
&& quality_of_protection
) *quality_of_protection
= (conf_state
? 0 : SECQOP_WRAP_NO_ENCRYPT
);
1359 return status_gss_to_sspi( ret
);
1362 FIXME( "(%lx %p %u %p)\n", context
, message
, message_seq_no
, quality_of_protection
);
1363 return SEC_E_UNSUPPORTED_FUNCTION
;
1367 static SECPKG_USER_FUNCTION_TABLE kerberos_user_table
=
1369 kerberos_SpInstanceInit
,
1370 NULL
, /* SpInitUserModeContext */
1371 kerberos_SpMakeSignature
,
1372 kerberos_SpVerifySignature
,
1373 kerberos_SpSealMessage
,
1374 kerberos_SpUnsealMessage
,
1375 NULL
, /* SpGetContextToken */
1376 NULL
, /* SpQueryContextAttributes */
1377 NULL
, /* SpCompleteAuthToken */
1378 NULL
, /* SpDeleteContext */
1379 NULL
, /* SpFormatCredentialsFn */
1380 NULL
, /* SpMarshallSupplementalCreds */
1381 NULL
, /* SpExportSecurityContext */
1382 NULL
/* SpImportSecurityContext */
1385 NTSTATUS NTAPI
SpUserModeInitialize(ULONG lsa_version
, PULONG package_version
,
1386 PSECPKG_USER_FUNCTION_TABLE
*table
, PULONG table_count
)
1388 TRACE("%#x,%p,%p,%p\n", lsa_version
, package_version
, table
, table_count
);
1390 *package_version
= SECPKG_INTERFACE_VERSION
;
1391 *table
= &kerberos_user_table
;
1394 return STATUS_SUCCESS
;