2 * Unix interface for libkrb5/libgssapi_krb5
4 * Copyright 2017 Dmitry Timoshkov
5 * Copyright 2017 George Popoff
6 * Copyright 2008 Robert Shearman for CodeWeavers
7 * Copyright 2017,2021 Hans Leidekker for CodeWeavers
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
29 #if defined(SONAME_LIBKRB5) && defined(SONAME_LIBGSSAPI_KRB5)
30 #include "wine/port.h"
33 #ifdef HAVE_KRB5_KRB5_H
34 # include <krb5/krb5.h>
36 #ifdef HAVE_GSSAPI_GSSAPI_H
37 # include <gssapi/gssapi.h>
39 #ifdef HAVE_GSSAPI_GSSAPI_EXT_H
40 # include <gssapi/gssapi_ext.h>
44 #define WIN32_NO_STATUS
53 #include "wine/debug.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(kerberos
);
57 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
59 static void *libkrb5_handle
;
61 #define MAKE_FUNCPTR(f) static typeof(f) * p_##f
62 MAKE_FUNCPTR( krb5_cc_close
);
63 MAKE_FUNCPTR( krb5_cc_default
);
64 MAKE_FUNCPTR( krb5_cc_end_seq_get
);
65 MAKE_FUNCPTR( krb5_cc_initialize
);
66 MAKE_FUNCPTR( krb5_cc_next_cred
);
67 MAKE_FUNCPTR( krb5_cc_start_seq_get
);
68 MAKE_FUNCPTR( krb5_cc_store_cred
);
69 MAKE_FUNCPTR( krb5_cccol_cursor_free
);
70 MAKE_FUNCPTR( krb5_cccol_cursor_new
);
71 MAKE_FUNCPTR( krb5_cccol_cursor_next
);
72 MAKE_FUNCPTR( krb5_decode_ticket
);
73 MAKE_FUNCPTR( krb5_free_context
);
74 MAKE_FUNCPTR( krb5_free_cred_contents
);
75 MAKE_FUNCPTR( krb5_free_principal
);
76 MAKE_FUNCPTR( krb5_free_ticket
);
77 MAKE_FUNCPTR( krb5_free_unparsed_name
);
78 MAKE_FUNCPTR( krb5_get_init_creds_opt_alloc
);
79 MAKE_FUNCPTR( krb5_get_init_creds_opt_free
);
80 MAKE_FUNCPTR( krb5_get_init_creds_opt_set_out_ccache
);
81 MAKE_FUNCPTR( krb5_get_init_creds_password
);
82 MAKE_FUNCPTR( krb5_init_context
);
83 MAKE_FUNCPTR( krb5_is_config_principal
);
84 MAKE_FUNCPTR( krb5_parse_name_flags
);
85 MAKE_FUNCPTR( krb5_unparse_name_flags
);
88 static BOOL
load_krb5(void)
90 if (!(libkrb5_handle
= dlopen( SONAME_LIBKRB5
, RTLD_NOW
)))
92 WARN_(winediag
)( "failed to load %s, Kerberos support will be disabled\n", SONAME_LIBKRB5
);
96 #define LOAD_FUNCPTR(f) \
97 if (!(p_##f = dlsym( libkrb5_handle, #f ))) \
99 ERR( "failed to load %s\n", #f ); \
103 LOAD_FUNCPTR( krb5_cc_close
)
104 LOAD_FUNCPTR( krb5_cc_default
)
105 LOAD_FUNCPTR( krb5_cc_end_seq_get
)
106 LOAD_FUNCPTR( krb5_cc_initialize
)
107 LOAD_FUNCPTR( krb5_cc_next_cred
)
108 LOAD_FUNCPTR( krb5_cc_start_seq_get
)
109 LOAD_FUNCPTR( krb5_cc_store_cred
)
110 LOAD_FUNCPTR( krb5_cccol_cursor_free
)
111 LOAD_FUNCPTR( krb5_cccol_cursor_new
)
112 LOAD_FUNCPTR( krb5_cccol_cursor_next
)
113 LOAD_FUNCPTR( krb5_decode_ticket
)
114 LOAD_FUNCPTR( krb5_free_context
)
115 LOAD_FUNCPTR( krb5_free_cred_contents
)
116 LOAD_FUNCPTR( krb5_free_principal
)
117 LOAD_FUNCPTR( krb5_free_ticket
)
118 LOAD_FUNCPTR( krb5_free_unparsed_name
)
119 LOAD_FUNCPTR( krb5_get_init_creds_opt_alloc
)
120 LOAD_FUNCPTR( krb5_get_init_creds_opt_free
)
121 LOAD_FUNCPTR( krb5_get_init_creds_opt_set_out_ccache
)
122 LOAD_FUNCPTR( krb5_get_init_creds_password
)
123 LOAD_FUNCPTR( krb5_init_context
)
124 LOAD_FUNCPTR( krb5_is_config_principal
)
125 LOAD_FUNCPTR( krb5_parse_name_flags
)
126 LOAD_FUNCPTR( krb5_unparse_name_flags
)
131 dlclose( libkrb5_handle
);
132 libkrb5_handle
= NULL
;
136 static void unload_krb5(void)
138 dlclose( libkrb5_handle
);
139 libkrb5_handle
= NULL
;
142 static NTSTATUS
krb5_error_to_status( krb5_error_code err
)
146 case 0: return STATUS_SUCCESS
;
147 default: return STATUS_UNSUCCESSFUL
; /* FIXME */
151 static WCHAR
*utf8_to_wstr( const char *src
)
153 ULONG dstlen
, srclen
= strlen( src
) + 1;
156 RtlUTF8ToUnicodeN( NULL
, 0, &dstlen
, src
, srclen
);
157 if ((dst
= RtlAllocateHeap( GetProcessHeap(), 0, dstlen
)))
158 RtlUTF8ToUnicodeN( dst
, dstlen
, &dstlen
, src
, srclen
);
162 static NTSTATUS
copy_tickets_from_cache( krb5_context ctx
, krb5_ccache cache
, struct ticket_list
*list
)
165 krb5_cc_cursor cursor
;
169 char *name_with_realm
, *name_without_realm
, *realm_name
;
170 WCHAR
*realm_nameW
, *name_without_realmW
;
172 if ((err
= p_krb5_cc_start_seq_get( ctx
, cache
, &cursor
))) return krb5_error_to_status( err
);
175 if ((err
= p_krb5_cc_next_cred( ctx
, cache
, &cursor
, &creds
)))
177 if (err
== KRB5_CC_END
)
178 status
= STATUS_SUCCESS
;
180 status
= krb5_error_to_status( err
);
184 if (p_krb5_is_config_principal( ctx
, creds
.server
))
186 p_krb5_free_cred_contents( ctx
, &creds
);
190 if (list
->count
== list
->allocated
)
192 KERB_TICKET_CACHE_INFO
*new_tickets
;
197 new_allocated
= list
->allocated
* 2;
198 new_tickets
= RtlReAllocateHeap( GetProcessHeap(), 0, list
->tickets
, sizeof(*new_tickets
) * new_allocated
);
203 new_tickets
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*new_tickets
) * new_allocated
);
207 p_krb5_free_cred_contents( ctx
, &creds
);
208 status
= STATUS_NO_MEMORY
;
211 list
->tickets
= new_tickets
;
212 list
->allocated
= new_allocated
;
215 if ((err
= p_krb5_unparse_name_flags( ctx
, creds
.server
, 0, &name_with_realm
)))
217 p_krb5_free_cred_contents( ctx
, &creds
);
218 status
= krb5_error_to_status( err
);
221 TRACE( "name_with_realm: %s\n", debugstr_a(name_with_realm
) );
223 if ((err
= p_krb5_unparse_name_flags( ctx
, creds
.server
, KRB5_PRINCIPAL_UNPARSE_NO_REALM
,
224 &name_without_realm
)))
226 p_krb5_free_unparsed_name( ctx
, name_with_realm
);
227 p_krb5_free_cred_contents( ctx
, &creds
);
228 status
= krb5_error_to_status( err
);
231 TRACE( "name_without_realm: %s\n", debugstr_a(name_without_realm
) );
233 name_without_realmW
= utf8_to_wstr( name_without_realm
);
234 RtlInitUnicodeString( &list
->tickets
[list
->count
].ServerName
, name_without_realmW
);
236 if (!(realm_name
= strchr( name_with_realm
, '@' )))
238 ERR( "wrong name with realm %s\n", debugstr_a(name_with_realm
) );
239 realm_name
= name_with_realm
;
243 /* realm_name - now contains only realm! */
244 realm_nameW
= utf8_to_wstr( realm_name
);
245 RtlInitUnicodeString( &list
->tickets
[list
->count
].RealmName
, realm_nameW
);
247 if (!creds
.times
.starttime
) creds
.times
.starttime
= creds
.times
.authtime
;
249 /* TODO: if krb5_is_config_principal = true */
250 RtlSecondsSince1970ToTime( creds
.times
.starttime
, &list
->tickets
[list
->count
].StartTime
);
251 RtlSecondsSince1970ToTime( creds
.times
.endtime
, &list
->tickets
[list
->count
].EndTime
);
252 RtlSecondsSince1970ToTime( creds
.times
.renew_till
, &list
->tickets
[list
->count
].RenewTime
);
254 list
->tickets
[list
->count
].TicketFlags
= creds
.ticket_flags
;
256 err
= p_krb5_decode_ticket( &creds
.ticket
, &ticket
);
257 p_krb5_free_unparsed_name( ctx
, name_with_realm
);
258 p_krb5_free_unparsed_name( ctx
, name_without_realm
);
259 p_krb5_free_cred_contents( ctx
, &creds
);
262 status
= krb5_error_to_status( err
);
266 list
->tickets
[list
->count
].EncryptionType
= ticket
->enc_part
.enctype
;
267 p_krb5_free_ticket( ctx
, ticket
);
271 p_krb5_cc_end_seq_get( ctx
, cache
, &cursor
);
275 static NTSTATUS CDECL
query_ticket_cache( struct ticket_list
*list
)
280 krb5_cccol_cursor cursor
= NULL
;
285 list
->tickets
= NULL
;
287 if ((err
= p_krb5_init_context( &ctx
))) return krb5_error_to_status( err
);
288 if ((err
= p_krb5_cccol_cursor_new( ctx
, &cursor
)))
290 status
= krb5_error_to_status( err
);
296 if ((err
= p_krb5_cccol_cursor_next( ctx
, cursor
, &cache
)))
298 status
= krb5_error_to_status( err
);
303 status
= copy_tickets_from_cache( ctx
, cache
, list
);
304 p_krb5_cc_close( ctx
, cache
);
305 if (status
!= STATUS_SUCCESS
) goto done
;
309 if (cursor
) p_krb5_cccol_cursor_free( ctx
, &cursor
);
310 if (ctx
) p_krb5_free_context( ctx
);
314 static void *libgssapi_krb5_handle
;
316 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
317 MAKE_FUNCPTR( gss_accept_sec_context
);
318 MAKE_FUNCPTR( gss_acquire_cred
);
319 MAKE_FUNCPTR( gss_delete_sec_context
);
320 MAKE_FUNCPTR( gss_display_status
);
321 MAKE_FUNCPTR( gss_get_mic
);
322 MAKE_FUNCPTR( gss_import_name
);
323 MAKE_FUNCPTR( gss_init_sec_context
);
324 MAKE_FUNCPTR( gss_inquire_context
);
325 MAKE_FUNCPTR( gss_release_buffer
);
326 MAKE_FUNCPTR( gss_release_cred
);
327 MAKE_FUNCPTR( gss_release_iov_buffer
);
328 MAKE_FUNCPTR( gss_release_name
);
329 MAKE_FUNCPTR( gss_unwrap
);
330 MAKE_FUNCPTR( gss_unwrap_iov
);
331 MAKE_FUNCPTR( gss_verify_mic
);
332 MAKE_FUNCPTR( gss_wrap
);
333 MAKE_FUNCPTR( gss_wrap_iov
);
336 static BOOL
load_gssapi_krb5(void)
338 if (!(libgssapi_krb5_handle
= dlopen( SONAME_LIBGSSAPI_KRB5
, RTLD_NOW
)))
340 WARN_(winediag
)( "failed to load %s, Kerberos support will be disabled\n", SONAME_LIBGSSAPI_KRB5
);
344 #define LOAD_FUNCPTR(f) \
345 if (!(p##f = dlsym( libgssapi_krb5_handle, #f ))) \
347 ERR( "failed to load %s\n", #f ); \
351 LOAD_FUNCPTR( gss_accept_sec_context
)
352 LOAD_FUNCPTR( gss_acquire_cred
)
353 LOAD_FUNCPTR( gss_delete_sec_context
)
354 LOAD_FUNCPTR( gss_display_status
)
355 LOAD_FUNCPTR( gss_get_mic
)
356 LOAD_FUNCPTR( gss_import_name
)
357 LOAD_FUNCPTR( gss_init_sec_context
)
358 LOAD_FUNCPTR( gss_inquire_context
)
359 LOAD_FUNCPTR( gss_release_buffer
)
360 LOAD_FUNCPTR( gss_release_cred
)
361 LOAD_FUNCPTR( gss_release_iov_buffer
)
362 LOAD_FUNCPTR( gss_release_name
)
363 LOAD_FUNCPTR( gss_unwrap
)
364 LOAD_FUNCPTR( gss_unwrap_iov
)
365 LOAD_FUNCPTR( gss_verify_mic
)
366 LOAD_FUNCPTR( gss_wrap
)
367 LOAD_FUNCPTR( gss_wrap_iov
)
372 dlclose( libgssapi_krb5_handle
);
373 libgssapi_krb5_handle
= NULL
;
377 static BOOL
is_dce_style_context( gss_ctx_id_t ctx
)
379 OM_uint32 ret
, minor_status
, flags
;
380 ret
= pgss_inquire_context( &minor_status
, ctx
, NULL
, NULL
, NULL
, NULL
, &flags
, NULL
, NULL
);
381 return (ret
== GSS_S_COMPLETE
&& (flags
& GSS_C_DCE_STYLE
));
384 static int get_buffer_index( SecBufferDesc
*desc
, DWORD type
)
387 if (!desc
) return -1;
388 for (i
= 0; i
< desc
->cBuffers
; i
++)
390 if (desc
->pBuffers
[i
].BufferType
== type
) return i
;
395 static NTSTATUS
status_gss_to_sspi( OM_uint32 status
)
399 case GSS_S_COMPLETE
: return SEC_E_OK
;
400 case GSS_S_BAD_MECH
: return SEC_E_SECPKG_NOT_FOUND
;
401 case GSS_S_BAD_SIG
: return SEC_E_MESSAGE_ALTERED
;
402 case GSS_S_NO_CRED
: return SEC_E_NO_CREDENTIALS
;
403 case GSS_S_NO_CONTEXT
: return SEC_E_INVALID_HANDLE
;
404 case GSS_S_DEFECTIVE_TOKEN
: return SEC_E_INVALID_TOKEN
;
405 case GSS_S_DEFECTIVE_CREDENTIAL
: return SEC_E_NO_CREDENTIALS
;
406 case GSS_S_CREDENTIALS_EXPIRED
: return SEC_E_CONTEXT_EXPIRED
;
407 case GSS_S_CONTEXT_EXPIRED
: return SEC_E_CONTEXT_EXPIRED
;
408 case GSS_S_BAD_QOP
: return SEC_E_QOP_NOT_SUPPORTED
;
409 case GSS_S_CONTINUE_NEEDED
: return SEC_I_CONTINUE_NEEDED
;
410 case GSS_S_DUPLICATE_TOKEN
: return SEC_E_INVALID_TOKEN
;
411 case GSS_S_OLD_TOKEN
: return SEC_E_INVALID_TOKEN
;
412 case GSS_S_UNSEQ_TOKEN
: return SEC_E_OUT_OF_SEQUENCE
;
413 case GSS_S_GAP_TOKEN
: return SEC_E_OUT_OF_SEQUENCE
;
414 case GSS_S_FAILURE
: return SEC_E_INTERNAL_ERROR
;
417 FIXME( "couldn't convert status 0x%08x to NTSTATUS\n", status
);
418 return SEC_E_INTERNAL_ERROR
;
422 static void trace_gss_status_ex( OM_uint32 code
, int type
)
424 OM_uint32 ret
, minor_status
;
426 OM_uint32 msg_ctx
= 0;
430 ret
= pgss_display_status( &minor_status
, code
, type
, GSS_C_NULL_OID
, &msg_ctx
, &buf
);
431 if (GSS_ERROR( ret
))
433 TRACE( "gss_display_status(0x%08x,%d) returned %08x minor status %08x\n", code
, type
, ret
, minor_status
);
436 TRACE( "GSS-API error: 0x%08x: %s\n", code
, debugstr_an(buf
.value
, buf
.length
) );
437 pgss_release_buffer( &minor_status
, &buf
);
438 if (!msg_ctx
) return;
442 static void trace_gss_status( OM_uint32 major_status
, OM_uint32 minor_status
)
444 if (TRACE_ON(kerberos
))
446 trace_gss_status_ex( major_status
, GSS_C_GSS_CODE
);
447 trace_gss_status_ex( minor_status
, GSS_C_MECH_CODE
);
451 static inline gss_ctx_id_t
ctxhandle_sspi_to_gss( LSA_SEC_HANDLE handle
)
453 return (gss_ctx_id_t
)handle
;
456 static inline gss_cred_id_t
credhandle_sspi_to_gss( LSA_SEC_HANDLE handle
)
458 return (gss_cred_id_t
)handle
;
461 static inline void ctxhandle_gss_to_sspi( gss_ctx_id_t handle
, LSA_SEC_HANDLE
*ctx
)
463 *ctx
= (LSA_SEC_HANDLE
)handle
;
466 static inline void credhandle_gss_to_sspi( gss_cred_id_t handle
, LSA_SEC_HANDLE
*cred
)
468 *cred
= (LSA_SEC_HANDLE
)handle
;
471 static void expirytime_gss_to_sspi( OM_uint32 expirytime
, TimeStamp
*timestamp
)
475 NtQuerySystemTime( &time
);
476 RtlSystemTimeToLocalTime( &time
, &time
);
477 timestamp
->LowPart
= time
.QuadPart
;
478 timestamp
->HighPart
= time
.QuadPart
>> 32;
481 static ULONG
flags_gss_to_asc_ret( ULONG flags
)
484 if (flags
& GSS_C_DELEG_FLAG
) ret
|= ASC_RET_DELEGATE
;
485 if (flags
& GSS_C_MUTUAL_FLAG
) ret
|= ASC_RET_MUTUAL_AUTH
;
486 if (flags
& GSS_C_REPLAY_FLAG
) ret
|= ASC_RET_REPLAY_DETECT
;
487 if (flags
& GSS_C_SEQUENCE_FLAG
) ret
|= ASC_RET_SEQUENCE_DETECT
;
488 if (flags
& GSS_C_CONF_FLAG
) ret
|= ASC_RET_CONFIDENTIALITY
;
489 if (flags
& GSS_C_INTEG_FLAG
) ret
|= ASC_RET_INTEGRITY
;
490 if (flags
& GSS_C_ANON_FLAG
) ret
|= ASC_RET_NULL_SESSION
;
491 if (flags
& GSS_C_DCE_STYLE
) ret
|= ASC_RET_USED_DCE_STYLE
;
492 if (flags
& GSS_C_IDENTIFY_FLAG
) ret
|= ASC_RET_IDENTIFY
;
496 NTSTATUS CDECL
accept_context( LSA_SEC_HANDLE credential
, LSA_SEC_HANDLE context
, SecBufferDesc
*input
,
497 LSA_SEC_HANDLE
*new_context
, SecBufferDesc
*output
, ULONG
*context_attr
,
500 OM_uint32 ret
, minor_status
, ret_flags
= 0, expiry_time
;
501 gss_cred_id_t cred_handle
= credhandle_sspi_to_gss( credential
);
502 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( context
);
503 gss_buffer_desc input_token
, output_token
;
506 if (!input
) input_token
.length
= 0;
509 if ((idx
= get_buffer_index( input
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
510 input_token
.length
= input
->pBuffers
[idx
].cbBuffer
;
511 input_token
.value
= input
->pBuffers
[idx
].pvBuffer
;
514 if ((idx
= get_buffer_index( output
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
515 output_token
.length
= 0;
516 output_token
.value
= NULL
;
518 ret
= pgss_accept_sec_context( &minor_status
, &ctx_handle
, cred_handle
, &input_token
, GSS_C_NO_CHANNEL_BINDINGS
,
519 NULL
, NULL
, &output_token
, &ret_flags
, &expiry_time
, NULL
);
520 TRACE( "gss_accept_sec_context returned %08x minor status %08x ret_flags %08x\n", ret
, minor_status
, ret_flags
);
521 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
522 if (ret
== GSS_S_COMPLETE
|| ret
== GSS_S_CONTINUE_NEEDED
)
524 if (output_token
.length
> output
->pBuffers
[idx
].cbBuffer
) /* FIXME: check if larger buffer exists */
526 TRACE( "buffer too small %lu > %u\n", (SIZE_T
)output_token
.length
, output
->pBuffers
[idx
].cbBuffer
);
527 pgss_release_buffer( &minor_status
, &output_token
);
528 pgss_delete_sec_context( &minor_status
, &ctx_handle
, GSS_C_NO_BUFFER
);
529 return SEC_E_BUFFER_TOO_SMALL
;
531 output
->pBuffers
[idx
].cbBuffer
= output_token
.length
;
532 memcpy( output
->pBuffers
[idx
].pvBuffer
, output_token
.value
, output_token
.length
);
533 pgss_release_buffer( &minor_status
, &output_token
);
535 ctxhandle_gss_to_sspi( ctx_handle
, new_context
);
536 if (context_attr
) *context_attr
= flags_gss_to_asc_ret( ret_flags
);
537 expirytime_gss_to_sspi( expiry_time
, expiry
);
540 return status_gss_to_sspi( ret
);
543 static NTSTATUS
init_creds( const char *user_at_domain
, const char *password
)
546 krb5_principal principal
= NULL
;
547 krb5_get_init_creds_opt
*options
= NULL
;
548 krb5_ccache cache
= NULL
;
552 if (!user_at_domain
) return STATUS_SUCCESS
;
553 if ((err
= p_krb5_init_context( &ctx
))) return krb5_error_to_status( err
);
554 if ((err
= p_krb5_parse_name_flags( ctx
, user_at_domain
, 0, &principal
))) goto done
;
555 if ((err
= p_krb5_cc_default( ctx
, &cache
))) goto done
;
556 if ((err
= p_krb5_get_init_creds_opt_alloc( ctx
, &options
))) goto done
;
557 if ((err
= p_krb5_get_init_creds_opt_set_out_ccache( ctx
, options
, cache
))) goto done
;
558 if ((err
= p_krb5_get_init_creds_password( ctx
, &creds
, principal
, password
, 0, NULL
, 0, NULL
, 0 ))) goto done
;
559 if ((err
= p_krb5_cc_initialize( ctx
, cache
, principal
))) goto done
;
560 if ((err
= p_krb5_cc_store_cred( ctx
, cache
, &creds
))) goto done
;
562 TRACE( "success\n" );
563 p_krb5_free_cred_contents( ctx
, &creds
);
566 if (cache
) p_krb5_cc_close( ctx
, cache
);
567 if (principal
) p_krb5_free_principal( ctx
, principal
);
568 if (options
) p_krb5_get_init_creds_opt_free( ctx
, options
);
569 p_krb5_free_context( ctx
);
570 return krb5_error_to_status( err
);
573 static NTSTATUS
import_name( const char *src
, gss_name_t
*dst
)
575 OM_uint32 ret
, minor_status
;
578 buf
.length
= strlen( src
);
579 buf
.value
= (void *)src
;
580 ret
= pgss_import_name( &minor_status
, &buf
, GSS_C_NO_OID
, dst
);
581 TRACE( "gss_import_name returned %08x minor status %08x\n", ret
, minor_status
);
582 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
583 return status_gss_to_sspi( ret
);
586 static NTSTATUS CDECL
acquire_credentials_handle( const char *principal
, ULONG credential_use
, const char *username
,
587 const char *password
, LSA_SEC_HANDLE
*credential
, TimeStamp
*expiry
)
589 OM_uint32 ret
, minor_status
, expiry_time
;
590 gss_name_t name
= GSS_C_NO_NAME
;
591 gss_cred_usage_t cred_usage
;
592 gss_cred_id_t cred_handle
;
595 switch (credential_use
)
597 case SECPKG_CRED_INBOUND
:
598 cred_usage
= GSS_C_ACCEPT
;
601 case SECPKG_CRED_OUTBOUND
:
602 if ((status
= init_creds( username
, password
)) != STATUS_SUCCESS
) return status
;
603 cred_usage
= GSS_C_INITIATE
;
607 FIXME( "SECPKG_CRED_BOTH not supported\n" );
608 return SEC_E_UNKNOWN_CREDENTIALS
;
611 if (principal
&& (status
= import_name( principal
, &name
))) return status
;
613 ret
= pgss_acquire_cred( &minor_status
, name
, GSS_C_INDEFINITE
, GSS_C_NULL_OID_SET
, cred_usage
, &cred_handle
,
614 NULL
, &expiry_time
);
615 TRACE( "gss_acquire_cred returned %08x minor status %08x\n", ret
, minor_status
);
616 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
617 if (ret
== GSS_S_COMPLETE
)
619 credhandle_gss_to_sspi( cred_handle
, credential
);
620 expirytime_gss_to_sspi( expiry_time
, expiry
);
623 if (name
!= GSS_C_NO_NAME
) pgss_release_name( &minor_status
, &name
);
624 return status_gss_to_sspi( ret
);
627 static NTSTATUS CDECL
delete_context( LSA_SEC_HANDLE context
)
629 OM_uint32 ret
, minor_status
;
630 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( context
);
632 ret
= pgss_delete_sec_context( &minor_status
, &ctx_handle
, GSS_C_NO_BUFFER
);
633 TRACE( "gss_delete_sec_context returned %08x minor status %08x\n", ret
, minor_status
);
634 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
635 return status_gss_to_sspi( ret
);
638 static NTSTATUS CDECL
free_credentials_handle( LSA_SEC_HANDLE handle
)
640 OM_uint32 ret
, minor_status
;
641 gss_cred_id_t cred
= credhandle_sspi_to_gss( handle
);
643 ret
= pgss_release_cred( &minor_status
, &cred
);
644 TRACE( "gss_release_cred returned %08x minor status %08x\n", ret
, minor_status
);
645 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
646 return status_gss_to_sspi( ret
);
649 static ULONG
flags_isc_req_to_gss( ULONG flags
)
652 if (flags
& ISC_REQ_DELEGATE
) ret
|= GSS_C_DELEG_FLAG
;
653 if (flags
& ISC_REQ_MUTUAL_AUTH
) ret
|= GSS_C_MUTUAL_FLAG
;
654 if (flags
& ISC_REQ_REPLAY_DETECT
) ret
|= GSS_C_REPLAY_FLAG
;
655 if (flags
& ISC_REQ_SEQUENCE_DETECT
) ret
|= GSS_C_SEQUENCE_FLAG
;
656 if (flags
& ISC_REQ_CONFIDENTIALITY
) ret
|= GSS_C_CONF_FLAG
;
657 if (flags
& ISC_REQ_INTEGRITY
) ret
|= GSS_C_INTEG_FLAG
;
658 if (flags
& ISC_REQ_NULL_SESSION
) ret
|= GSS_C_ANON_FLAG
;
659 if (flags
& ISC_REQ_USE_DCE_STYLE
) ret
|= GSS_C_DCE_STYLE
;
660 if (flags
& ISC_REQ_IDENTIFY
) ret
|= GSS_C_IDENTIFY_FLAG
;
664 static ULONG
flags_gss_to_isc_ret( ULONG flags
)
667 if (flags
& GSS_C_DELEG_FLAG
) ret
|= ISC_RET_DELEGATE
;
668 if (flags
& GSS_C_MUTUAL_FLAG
) ret
|= ISC_RET_MUTUAL_AUTH
;
669 if (flags
& GSS_C_REPLAY_FLAG
) ret
|= ISC_RET_REPLAY_DETECT
;
670 if (flags
& GSS_C_SEQUENCE_FLAG
) ret
|= ISC_RET_SEQUENCE_DETECT
;
671 if (flags
& GSS_C_CONF_FLAG
) ret
|= ISC_RET_CONFIDENTIALITY
;
672 if (flags
& GSS_C_INTEG_FLAG
) ret
|= ISC_RET_INTEGRITY
;
673 if (flags
& GSS_C_ANON_FLAG
) ret
|= ISC_RET_NULL_SESSION
;
674 if (flags
& GSS_C_DCE_STYLE
) ret
|= ISC_RET_USED_DCE_STYLE
;
675 if (flags
& GSS_C_IDENTIFY_FLAG
) ret
|= ISC_RET_IDENTIFY
;
679 static NTSTATUS CDECL
initialize_context( LSA_SEC_HANDLE credential
, LSA_SEC_HANDLE context
, const char *target_name
,
680 ULONG context_req
, SecBufferDesc
*input
, LSA_SEC_HANDLE
*new_context
,
681 SecBufferDesc
*output
, ULONG
*context_attr
, TimeStamp
*expiry
)
683 OM_uint32 ret
, minor_status
, ret_flags
= 0, expiry_time
, req_flags
= flags_isc_req_to_gss( context_req
);
684 gss_cred_id_t cred_handle
= credhandle_sspi_to_gss( credential
);
685 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( context
);
686 gss_buffer_desc input_token
, output_token
;
687 gss_name_t target
= GSS_C_NO_NAME
;
691 if ((idx
= get_buffer_index( input
, SECBUFFER_TOKEN
)) == -1) input_token
.length
= 0;
694 input_token
.length
= input
->pBuffers
[idx
].cbBuffer
;
695 input_token
.value
= input
->pBuffers
[idx
].pvBuffer
;
698 if ((idx
= get_buffer_index( output
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
699 output_token
.length
= 0;
700 output_token
.value
= NULL
;
702 if (target_name
&& (status
= import_name( target_name
, &target
))) return status
;
704 ret
= pgss_init_sec_context( &minor_status
, cred_handle
, &ctx_handle
, target
, GSS_C_NO_OID
, req_flags
, 0,
705 GSS_C_NO_CHANNEL_BINDINGS
, &input_token
, NULL
, &output_token
, &ret_flags
,
707 TRACE( "gss_init_sec_context returned %08x minor status %08x ret_flags %08x\n", ret
, minor_status
, ret_flags
);
708 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
709 if (ret
== GSS_S_COMPLETE
|| ret
== GSS_S_CONTINUE_NEEDED
)
711 if (output_token
.length
> output
->pBuffers
[idx
].cbBuffer
) /* FIXME: check if larger buffer exists */
713 TRACE( "buffer too small %lu > %u\n", (SIZE_T
)output_token
.length
, output
->pBuffers
[idx
].cbBuffer
);
714 pgss_release_buffer( &minor_status
, &output_token
);
715 pgss_delete_sec_context( &minor_status
, &ctx_handle
, GSS_C_NO_BUFFER
);
716 return SEC_E_INCOMPLETE_MESSAGE
;
718 output
->pBuffers
[idx
].cbBuffer
= output_token
.length
;
719 memcpy( output
->pBuffers
[idx
].pvBuffer
, output_token
.value
, output_token
.length
);
720 pgss_release_buffer( &minor_status
, &output_token
);
722 ctxhandle_gss_to_sspi( ctx_handle
, new_context
);
723 if (context_attr
) *context_attr
= flags_gss_to_isc_ret( ret_flags
);
724 expirytime_gss_to_sspi( expiry_time
, expiry
);
727 if (target
!= GSS_C_NO_NAME
) pgss_release_name( &minor_status
, &target
);
728 return status_gss_to_sspi( ret
);
731 static NTSTATUS CDECL
make_signature( LSA_SEC_HANDLE context
, SecBufferDesc
*msg
)
733 OM_uint32 ret
, minor_status
;
734 gss_buffer_desc data_buffer
, token_buffer
;
735 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( context
);
736 int data_idx
, token_idx
;
738 /* FIXME: multiple data buffers, read-only buffers */
739 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
740 data_buffer
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
741 data_buffer
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
743 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
744 token_buffer
.length
= 0;
745 token_buffer
.value
= NULL
;
747 ret
= pgss_get_mic( &minor_status
, ctx_handle
, GSS_C_QOP_DEFAULT
, &data_buffer
, &token_buffer
);
748 TRACE( "gss_get_mic returned %08x minor status %08x\n", ret
, minor_status
);
749 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
750 if (ret
== GSS_S_COMPLETE
)
752 memcpy( msg
->pBuffers
[token_idx
].pvBuffer
, token_buffer
.value
, token_buffer
.length
);
753 msg
->pBuffers
[token_idx
].cbBuffer
= token_buffer
.length
;
754 pgss_release_buffer( &minor_status
, &token_buffer
);
757 return status_gss_to_sspi( ret
);
760 #define KERBEROS_MAX_SIGNATURE 37
761 #define KERBEROS_SECURITY_TRAILER 49
762 #define KERBEROS_MAX_SIGNATURE_DCE 28
763 #define KERBEROS_SECURITY_TRAILER_DCE 76
765 static NTSTATUS CDECL
query_context_attributes( LSA_SEC_HANDLE context
, ULONG attr
, void *buf
)
769 case SECPKG_ATTR_SIZES
:
771 SecPkgContext_Sizes
*sizes
= (SecPkgContext_Sizes
*)buf
;
772 ULONG size_max_signature
, size_security_trailer
;
773 gss_ctx_id_t ctx
= ctxhandle_sspi_to_gss( context
);
775 if (is_dce_style_context( ctx
))
777 size_max_signature
= KERBEROS_MAX_SIGNATURE_DCE
;
778 size_security_trailer
= KERBEROS_SECURITY_TRAILER_DCE
;
782 size_max_signature
= KERBEROS_MAX_SIGNATURE
;
783 size_security_trailer
= KERBEROS_SECURITY_TRAILER
;
785 sizes
->cbMaxToken
= KERBEROS_MAX_BUF
;
786 sizes
->cbMaxSignature
= size_max_signature
;
787 sizes
->cbBlockSize
= 1;
788 sizes
->cbSecurityTrailer
= size_security_trailer
;
792 FIXME( "unhandled attribute %u\n", attr
);
796 return SEC_E_UNSUPPORTED_FUNCTION
;
799 static NTSTATUS
seal_message_vector( gss_ctx_id_t ctx
, SecBufferDesc
*msg
, ULONG qop
)
801 gss_iov_buffer_desc iov
[4];
802 OM_uint32 ret
, minor_status
;
803 int token_idx
, data_idx
, conf_flag
, conf_state
;
806 conf_flag
= 1; /* confidentiality + integrity */
807 else if (qop
== SECQOP_WRAP_NO_ENCRYPT
)
808 conf_flag
= 0; /* only integrity */
811 FIXME( "QOP %08x not supported\n", qop
);
812 return SEC_E_UNSUPPORTED_FUNCTION
;
815 /* FIXME: multiple data buffers, read-only buffers */
816 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
817 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
819 iov
[0].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
820 iov
[0].buffer
.length
= 0;
821 iov
[0].buffer
.value
= NULL
;
823 iov
[1].type
= GSS_IOV_BUFFER_TYPE_DATA
;
824 iov
[1].buffer
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
825 iov
[1].buffer
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
827 iov
[2].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
828 iov
[2].buffer
.length
= 0;
829 iov
[2].buffer
.value
= NULL
;
831 iov
[3].type
= GSS_IOV_BUFFER_TYPE_HEADER
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
832 iov
[3].buffer
.length
= 0;
833 iov
[3].buffer
.value
= NULL
;
835 ret
= pgss_wrap_iov( &minor_status
, ctx
, conf_flag
, GSS_C_QOP_DEFAULT
, &conf_state
, iov
, 4 );
836 TRACE( "gss_wrap_iov returned %08x minor status %08x\n", ret
, minor_status
);
837 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
838 if (ret
== GSS_S_COMPLETE
)
840 memcpy( msg
->pBuffers
[token_idx
].pvBuffer
, iov
[3].buffer
.value
, iov
[3].buffer
.length
);
841 msg
->pBuffers
[token_idx
].cbBuffer
= iov
[3].buffer
.length
;
842 pgss_release_iov_buffer( &minor_status
, iov
, 4 );
845 return status_gss_to_sspi( ret
);
848 static NTSTATUS
seal_message_no_vector( gss_ctx_id_t ctx
, SecBufferDesc
*msg
, ULONG qop
)
850 gss_buffer_desc input
, output
;
851 OM_uint32 ret
, minor_status
;
852 int token_idx
, data_idx
, conf_flag
, conf_state
;
855 conf_flag
= 1; /* confidentiality + integrity */
856 else if (qop
== SECQOP_WRAP_NO_ENCRYPT
)
857 conf_flag
= 0; /* only integrity */
860 FIXME( "QOP %08x not supported\n", qop
);
861 return SEC_E_UNSUPPORTED_FUNCTION
;
864 /* FIXME: multiple data buffers, read-only buffers */
865 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
866 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
868 input
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
869 input
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
871 ret
= pgss_wrap( &minor_status
, ctx
, conf_flag
, GSS_C_QOP_DEFAULT
, &input
, &conf_state
, &output
);
872 TRACE( "gss_wrap returned %08x minor status %08x\n", ret
, minor_status
);
873 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
874 if (ret
== GSS_S_COMPLETE
)
876 DWORD len_data
= msg
->pBuffers
[data_idx
].cbBuffer
, len_token
= msg
->pBuffers
[token_idx
].cbBuffer
;
877 if (len_token
< output
.length
- len_data
)
879 TRACE( "buffer too small %lu > %u\n", (SIZE_T
)output
.length
- len_data
, len_token
);
880 pgss_release_buffer( &minor_status
, &output
);
881 return SEC_E_BUFFER_TOO_SMALL
;
883 memcpy( msg
->pBuffers
[data_idx
].pvBuffer
, output
.value
, len_data
);
884 memcpy( msg
->pBuffers
[token_idx
].pvBuffer
, (char *)output
.value
+ len_data
, output
.length
- len_data
);
885 msg
->pBuffers
[token_idx
].cbBuffer
= output
.length
- len_data
;
886 pgss_release_buffer( &minor_status
, &output
);
889 return status_gss_to_sspi( ret
);
892 static NTSTATUS CDECL
seal_message( LSA_SEC_HANDLE context
, SecBufferDesc
*msg
, ULONG qop
)
894 gss_ctx_id_t ctx
= ctxhandle_sspi_to_gss( context
);
896 if (is_dce_style_context( ctx
)) return seal_message_vector( ctx
, msg
, qop
);
897 return seal_message_no_vector( ctx
, msg
, qop
);
900 static NTSTATUS
unseal_message_vector( gss_ctx_id_t ctx
, SecBufferDesc
*msg
, ULONG
*qop
)
902 gss_iov_buffer_desc iov
[4];
903 OM_uint32 ret
, minor_status
;
904 int token_idx
, data_idx
, conf_state
;
906 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
907 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
909 iov
[0].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
;
910 iov
[0].buffer
.length
= 0;
911 iov
[0].buffer
.value
= NULL
;
913 iov
[1].type
= GSS_IOV_BUFFER_TYPE_DATA
;
914 iov
[1].buffer
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
915 iov
[1].buffer
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
917 iov
[2].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
;
918 iov
[2].buffer
.length
= 0;
919 iov
[2].buffer
.value
= NULL
;
921 iov
[3].type
= GSS_IOV_BUFFER_TYPE_HEADER
;
922 iov
[3].buffer
.length
= msg
->pBuffers
[token_idx
].cbBuffer
;
923 iov
[3].buffer
.value
= msg
->pBuffers
[token_idx
].pvBuffer
;
925 ret
= pgss_unwrap_iov( &minor_status
, ctx
, &conf_state
, NULL
, iov
, 4 );
926 TRACE( "gss_unwrap_iov returned %08x minor status %08x\n", ret
, minor_status
);
927 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
928 if (ret
== GSS_S_COMPLETE
&& qop
)
930 *qop
= (conf_state
? 0 : SECQOP_WRAP_NO_ENCRYPT
);
932 return status_gss_to_sspi( ret
);
935 static NTSTATUS
unseal_message_no_vector( gss_ctx_id_t ctx
, SecBufferDesc
*msg
, ULONG
*qop
)
937 gss_buffer_desc input
, output
;
938 OM_uint32 ret
, minor_status
;
939 int token_idx
, data_idx
, conf_state
;
940 DWORD len_data
, len_token
;
942 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
943 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
945 len_data
= msg
->pBuffers
[data_idx
].cbBuffer
;
946 len_token
= msg
->pBuffers
[token_idx
].cbBuffer
;
948 input
.length
= len_data
+ len_token
;
949 if (!(input
.value
= RtlAllocateHeap( GetProcessHeap(), 0, input
.length
))) return SEC_E_INSUFFICIENT_MEMORY
;
950 memcpy( input
.value
, msg
->pBuffers
[data_idx
].pvBuffer
, len_data
);
951 memcpy( (char *)input
.value
+ len_data
, msg
->pBuffers
[token_idx
].pvBuffer
, len_token
);
953 ret
= pgss_unwrap( &minor_status
, ctx
, &input
, &output
, &conf_state
, NULL
);
954 RtlFreeHeap( GetProcessHeap(), 0, input
.value
);
955 TRACE( "gss_unwrap returned %08x minor status %08x\n", ret
, minor_status
);
956 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
957 if (ret
== GSS_S_COMPLETE
)
959 if (qop
) *qop
= (conf_state
? 0 : SECQOP_WRAP_NO_ENCRYPT
);
960 memcpy( msg
->pBuffers
[data_idx
].pvBuffer
, output
.value
, len_data
);
961 pgss_release_buffer( &minor_status
, &output
);
964 return status_gss_to_sspi( ret
);
967 static NTSTATUS CDECL
unseal_message( LSA_SEC_HANDLE context
, SecBufferDesc
*msg
, ULONG
*qop
)
969 gss_ctx_id_t ctx
= ctxhandle_sspi_to_gss( context
);
971 if (is_dce_style_context( ctx
)) return unseal_message_vector( ctx
, msg
, qop
);
972 return unseal_message_no_vector( ctx
, msg
, qop
);
975 static NTSTATUS CDECL
verify_signature( LSA_SEC_HANDLE context
, SecBufferDesc
*msg
, ULONG
*qop
)
977 OM_uint32 ret
, minor_status
;
978 gss_buffer_desc data_buffer
, token_buffer
;
979 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( context
);
980 int data_idx
, token_idx
;
982 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
983 data_buffer
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
984 data_buffer
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
986 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
987 token_buffer
.length
= msg
->pBuffers
[token_idx
].cbBuffer
;
988 token_buffer
.value
= msg
->pBuffers
[token_idx
].pvBuffer
;
990 ret
= pgss_verify_mic( &minor_status
, ctx_handle
, &data_buffer
, &token_buffer
, NULL
);
991 TRACE( "gss_verify_mic returned %08x minor status %08x\n", ret
, minor_status
);
992 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
993 if (ret
== GSS_S_COMPLETE
&& qop
) *qop
= 0;
995 return status_gss_to_sspi( ret
);
998 static const struct krb5_funcs funcs
=
1001 acquire_credentials_handle
,
1003 free_credentials_handle
,
1006 query_context_attributes
,
1013 NTSTATUS CDECL
__wine_init_unix_lib( HMODULE module
, DWORD reason
, const void *ptr_in
, void *ptr_out
)
1015 if (reason
!= DLL_PROCESS_ATTACH
) return STATUS_SUCCESS
;
1016 if (load_krb5() && load_gssapi_krb5())
1018 *(const struct krb5_funcs
**)ptr_out
= &funcs
;
1019 return STATUS_SUCCESS
;
1021 if (libkrb5_handle
) unload_krb5();
1022 return STATUS_DLL_NOT_FOUND
;
1024 #endif /* defined(SONAME_LIBKRB5) && defined(SONAME_LIBGSSAPI_KRB5) */